diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index 883693031e..05db95917e 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -358,26 +358,9 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "MultiplySrc" composition equation. - /// - public class MultiplySrc : PixelBlender - { - /// - /// 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 TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -389,14 +372,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -404,7 +405,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -454,18 +469,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -473,7 +507,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -522,6 +571,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -541,11 +591,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -553,7 +619,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -578,31 +646,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -612,6 +691,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -636,10 +716,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -647,7 +743,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -672,43 +770,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "AddSrc" composition equation. + /// A pixel blender that implements the "MultiplySrc" composition equation. /// - public class AddSrc : PixelBlender + public class MultiplySrc : PixelBlender { /// /// Gets the static instance of this blender. /// - public static AddSrc Instance { get; } = new AddSrc(); + public static MultiplySrc Instance { get; } = new MultiplySrc(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -728,7 +837,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -888,7 +997,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -924,14 +1033,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -982,7 +1091,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -1017,37 +1126,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "SubtractSrc" composition equation. - /// - public class SubtractSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrc Instance { get; } = new SubtractSrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -1059,14 +1151,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -1074,7 +1184,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -1124,18 +1248,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -1143,7 +1286,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -1192,6 +1350,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -1211,11 +1370,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -1223,7 +1398,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -1248,31 +1425,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -1282,6 +1470,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -1306,10 +1495,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -1317,7 +1522,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -1342,43 +1549,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "ScreenSrc" composition equation. + /// A pixel blender that implements the "AddSrc" composition equation. /// - public class ScreenSrc : PixelBlender + public class AddSrc : PixelBlender { /// /// Gets the static instance of this blender. /// - public static ScreenSrc Instance { get; } = new ScreenSrc(); + public static AddSrc Instance { get; } = new AddSrc(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1398,7 +1616,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -1558,7 +1776,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -1594,14 +1812,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -1652,7 +1870,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -1687,37 +1905,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "DarkenSrc" composition equation. - /// - public class DarkenSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrc Instance { get; } = new DarkenSrc(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -1729,14 +1930,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -1744,7 +1963,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -1794,18 +2027,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -1813,7 +2065,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -1862,6 +2129,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -1881,11 +2149,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -1893,7 +2177,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -1918,31 +2204,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -1952,6 +2249,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -1976,10 +2274,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -1987,7 +2301,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -2012,43 +2328,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "LightenSrc" composition equation. + /// A pixel blender that implements the "SubtractSrc" composition equation. /// - public class LightenSrc : PixelBlender + public class SubtractSrc : PixelBlender { /// /// Gets the static instance of this blender. /// - public static LightenSrc Instance { get; } = new LightenSrc(); + public static SubtractSrc Instance { get; } = new SubtractSrc(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2068,7 +2395,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -2228,7 +2555,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -2264,14 +2591,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -2322,7 +2649,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -2357,37 +2684,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "OverlaySrc" composition equation. - /// - public class OverlaySrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrc Instance { get; } = new OverlaySrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -2399,14 +2709,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -2414,7 +2742,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -2464,18 +2806,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -2483,7 +2844,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -2532,6 +2908,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -2551,11 +2928,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -2563,7 +2956,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -2588,31 +2983,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -2622,6 +3028,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -2646,10 +3053,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -2657,7 +3080,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -2682,43 +3107,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "HardLightSrc" composition equation. + /// A pixel blender that implements the "ScreenSrc" composition equation. /// - public class HardLightSrc : PixelBlender + public class ScreenSrc : PixelBlender { /// /// Gets the static instance of this blender. /// - public static HardLightSrc Instance { get; } = new HardLightSrc(); + public static ScreenSrc Instance { get; } = new ScreenSrc(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2738,7 +3174,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -2898,7 +3334,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -2934,14 +3370,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -2992,7 +3428,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -3027,37 +3463,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "NormalSrcAtop" composition equation. - /// - public class NormalSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcAtop Instance { get; } = new NormalSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -3069,14 +3488,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -3084,7 +3521,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -3134,18 +3585,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -3153,7 +3623,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -3202,6 +3687,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -3221,11 +3707,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -3233,7 +3735,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -3258,31 +3762,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -3292,6 +3807,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -3316,10 +3832,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -3327,7 +3859,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -3352,43 +3886,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "MultiplySrcAtop" composition equation. + /// A pixel blender that implements the "DarkenSrc" composition equation. /// - public class MultiplySrcAtop : PixelBlender + public class DarkenSrc : PixelBlender { /// /// Gets the static instance of this blender. /// - public static MultiplySrcAtop Instance { get; } = new MultiplySrcAtop(); + public static DarkenSrc Instance { get; } = new DarkenSrc(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -3408,7 +3953,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "LightenSrc" composition equation. + /// + public class LightenSrc : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static LightenSrc Instance { get; } = new LightenSrc(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "OverlaySrc" composition equation. + /// + public class OverlaySrc : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static OverlaySrc Instance { get; } = new OverlaySrc(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "HardLightSrc" composition equation. + /// + public class HardLightSrc : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static HardLightSrc Instance { get; } = new HardLightSrc(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "NormalSrcAtop" composition equation. + /// + public class NormalSrcAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static NormalSrcAtop Instance { get; } = new NormalSrcAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "MultiplySrcAtop" composition equation. + /// + public class MultiplySrcAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static MultiplySrcAtop Instance { get; } = new MultiplySrcAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "AddSrcAtop" composition equation. + /// + public class AddSrcAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static AddSrcAtop Instance { get; } = new AddSrcAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "SubtractSrcAtop" composition equation. + /// + public class SubtractSrcAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static SubtractSrcAtop Instance { get; } = new SubtractSrcAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "ScreenSrcAtop" composition equation. + /// + public class ScreenSrcAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static ScreenSrcAtop Instance { get; } = new ScreenSrcAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "DarkenSrcAtop" composition equation. + /// + public class DarkenSrcAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static DarkenSrcAtop Instance { get; } = new DarkenSrcAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "LightenSrcAtop" composition equation. + /// + public class LightenSrcAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static LightenSrcAtop Instance { get; } = new LightenSrcAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "OverlaySrcAtop" composition equation. + /// + public class OverlaySrcAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static OverlaySrcAtop Instance { get; } = new OverlaySrcAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "HardLightSrcAtop" composition equation. + /// + public class HardLightSrcAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static HardLightSrcAtop Instance { get; } = new HardLightSrcAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "NormalSrcOver" composition equation. + /// + public class NormalSrcOver : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static NormalSrcOver Instance { get; } = new NormalSrcOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "MultiplySrcOver" composition equation. + /// + public class MultiplySrcOver : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static MultiplySrcOver Instance { get; } = new MultiplySrcOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "AddSrcOver" composition equation. + /// + public class AddSrcOver : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static AddSrcOver Instance { get; } = new AddSrcOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "SubtractSrcOver" composition equation. + /// + public class SubtractSrcOver : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static SubtractSrcOver Instance { get; } = new SubtractSrcOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "ScreenSrcOver" composition equation. + /// + public class ScreenSrcOver : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static ScreenSrcOver Instance { get; } = new ScreenSrcOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "DarkenSrcOver" composition equation. + /// + public class DarkenSrcOver : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static DarkenSrcOver Instance { get; } = new DarkenSrcOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "LightenSrcOver" composition equation. + /// + public class LightenSrcOver : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static LightenSrcOver Instance { get; } = new LightenSrcOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "OverlaySrcOver" composition equation. + /// + public class OverlaySrcOver : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static OverlaySrcOver Instance { get; } = new OverlaySrcOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "HardLightSrcOver" composition equation. + /// + public class HardLightSrcOver : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static HardLightSrcOver Instance { get; } = new HardLightSrcOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "NormalSrcIn" composition equation. + /// + public class NormalSrcIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static NormalSrcIn Instance { get; } = new NormalSrcIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "MultiplySrcIn" composition equation. + /// + public class MultiplySrcIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static MultiplySrcIn Instance { get; } = new MultiplySrcIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "AddSrcIn" composition equation. + /// + public class AddSrcIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static AddSrcIn Instance { get; } = new AddSrcIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "SubtractSrcIn" composition equation. + /// + public class SubtractSrcIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static SubtractSrcIn Instance { get; } = new SubtractSrcIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "ScreenSrcIn" composition equation. + /// + public class ScreenSrcIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static ScreenSrcIn Instance { get; } = new ScreenSrcIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "DarkenSrcIn" composition equation. + /// + public class DarkenSrcIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static DarkenSrcIn Instance { get; } = new DarkenSrcIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "LightenSrcIn" composition equation. + /// + public class LightenSrcIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static LightenSrcIn Instance { get; } = new LightenSrcIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "OverlaySrcIn" composition equation. + /// + public class OverlaySrcIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static OverlaySrcIn Instance { get; } = new OverlaySrcIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "HardLightSrcIn" composition equation. + /// + public class HardLightSrcIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static HardLightSrcIn Instance { get; } = new HardLightSrcIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "NormalSrcOut" composition equation. + /// + public class NormalSrcOut : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static NormalSrcOut Instance { get; } = new NormalSrcOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "MultiplySrcOut" composition equation. + /// + public class MultiplySrcOut : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static MultiplySrcOut Instance { get; } = new MultiplySrcOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "AddSrcOut" composition equation. + /// + public class AddSrcOut : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static AddSrcOut Instance { get; } = new AddSrcOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "SubtractSrcOut" composition equation. + /// + public class SubtractSrcOut : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static SubtractSrcOut Instance { get; } = new SubtractSrcOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "ScreenSrcOut" composition equation. + /// + public class ScreenSrcOut : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static ScreenSrcOut Instance { get; } = new ScreenSrcOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "DarkenSrcOut" composition equation. + /// + public class DarkenSrcOut : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static DarkenSrcOut Instance { get; } = new DarkenSrcOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "LightenSrcOut" composition equation. + /// + public class LightenSrcOut : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static LightenSrcOut Instance { get; } = new LightenSrcOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "OverlaySrcOut" composition equation. + /// + public class OverlaySrcOut : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static OverlaySrcOut Instance { get; } = new OverlaySrcOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "HardLightSrcOut" composition equation. + /// + public class HardLightSrcOut : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static HardLightSrcOut Instance { get; } = new HardLightSrcOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "NormalDest" composition equation. + /// + public class NormalDest : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static NormalDest Instance { get; } = new NormalDest(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDest(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDest(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDest(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "MultiplyDest" composition equation. + /// + public class MultiplyDest : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static MultiplyDest Instance { get; } = new MultiplyDest(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "AddDest" composition equation. + /// + public class AddDest : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static AddDest Instance { get; } = new AddDest(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDest(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDest(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDest(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "SubtractDest" composition equation. + /// + public class SubtractDest : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static SubtractDest Instance { get; } = new SubtractDest(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "ScreenDest" composition equation. + /// + public class ScreenDest : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static ScreenDest Instance { get; } = new ScreenDest(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "DarkenDest" composition equation. + /// + public class DarkenDest : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static DarkenDest Instance { get; } = new DarkenDest(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "LightenDest" composition equation. + /// + public class LightenDest : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static LightenDest Instance { get; } = new LightenDest(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDest(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDest(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDest(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "OverlayDest" composition equation. + /// + public class OverlayDest : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static OverlayDest Instance { get; } = new OverlayDest(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.OverlayDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "HardLightDest" composition equation. + /// + public class HardLightDest : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static HardLightDest Instance { get; } = new HardLightDest(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "NormalDestAtop" composition equation. + /// + public class NormalDestAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static NormalDestAtop Instance { get; } = new NormalDestAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.NormalDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "MultiplyDestAtop" composition equation. + /// + public class MultiplyDestAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static MultiplyDestAtop Instance { get; } = new MultiplyDestAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "AddDestAtop" composition equation. + /// + public class AddDestAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static AddDestAtop Instance { get; } = new AddDestAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.AddDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "SubtractDestAtop" composition equation. + /// + public class SubtractDestAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static SubtractDestAtop Instance { get; } = new SubtractDestAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "ScreenDestAtop" composition equation. + /// + public class ScreenDestAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static ScreenDestAtop Instance { get; } = new ScreenDestAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.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 = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source, amount); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -3488,7 +45587,110 @@ protected override void BlendFunction(Span destination, ReadOnlySpan= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.ScreenDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } @@ -3499,34 +45701,48 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -3537,6 +45753,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -3556,11 +45773,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -3568,7 +45801,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -3593,31 +45828,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -3627,6 +45873,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -3651,10 +45898,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -3662,7 +45925,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -3687,43 +45952,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "AddSrcAtop" composition equation. + /// A pixel blender that implements the "DarkenDestAtop" composition equation. /// - public class AddSrcAtop : PixelBlender + public class DarkenDestAtop : PixelBlender { /// /// Gets the static instance of this blender. /// - public static AddSrcAtop Instance { get; } = new AddSrcAtop(); + public static DarkenDestAtop Instance { get; } = new DarkenDestAtop(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -3743,7 +46019,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -3903,7 +46179,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -3939,14 +46215,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -3997,7 +46273,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -4032,33 +46308,477 @@ protected override void BlendFunction(Span destination, ReadOnlySpan + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "SubtractSrcAtop" composition equation. + /// A pixel blender that implements the "LightenDestAtop" composition equation. /// - public class SubtractSrcAtop : PixelBlender + public class LightenDestAtop : PixelBlender { /// /// Gets the static instance of this blender. /// - public static SubtractSrcAtop Instance { get; } = new SubtractSrcAtop(); + public static LightenDestAtop Instance { get; } = new LightenDestAtop(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -4078,7 +46798,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -4238,7 +46958,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -4274,14 +46994,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -4332,7 +47052,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -4367,37 +47087,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "ScreenSrcAtop" composition equation. - /// - public class ScreenSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcAtop Instance { get; } = new ScreenSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -4409,14 +47112,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -4424,7 +47145,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -4474,18 +47209,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -4493,7 +47247,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.LightenDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.LightenDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -4542,6 +47311,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -4561,11 +47331,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -4573,7 +47359,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -4598,31 +47386,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -4632,6 +47431,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -4656,10 +47456,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -4667,7 +47483,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -4692,43 +47510,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "DarkenSrcAtop" composition equation. + /// A pixel blender that implements the "OverlayDestAtop" composition equation. /// - public class DarkenSrcAtop : PixelBlender + public class OverlayDestAtop : PixelBlender { /// /// Gets the static instance of this blender. /// - public static DarkenSrcAtop Instance { get; } = new DarkenSrcAtop(); + public static OverlayDestAtop Instance { get; } = new OverlayDestAtop(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -4748,7 +47577,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -4908,7 +47737,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -4944,14 +47773,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -5002,7 +47831,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -5037,37 +47866,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "LightenSrcAtop" composition equation. - /// - public class LightenSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcAtop Instance { get; } = new LightenSrcAtop(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -5079,14 +47891,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -5094,7 +47924,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -5144,18 +47988,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -5163,7 +48026,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.OverlayDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.OverlayDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -5212,6 +48090,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -5231,11 +48110,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -5243,7 +48138,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -5268,31 +48165,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -5302,6 +48210,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -5326,10 +48235,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -5337,7 +48262,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -5362,43 +48289,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "OverlaySrcAtop" composition equation. + /// A pixel blender that implements the "HardLightDestAtop" composition equation. /// - public class OverlaySrcAtop : PixelBlender + public class HardLightDestAtop : PixelBlender { /// /// Gets the static instance of this blender. /// - public static OverlaySrcAtop Instance { get; } = new OverlaySrcAtop(); + public static HardLightDestAtop Instance { get; } = new HardLightDestAtop(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -5418,7 +48356,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -5578,7 +48516,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -5614,14 +48552,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -5672,7 +48610,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -5707,37 +48645,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "HardLightSrcAtop" composition equation. - /// - public class HardLightSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcAtop Instance { get; } = new HardLightSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -5749,14 +48670,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -5764,7 +48703,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -5814,18 +48767,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -5833,7 +48805,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.HardLightDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.HardLightDestAtop(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -5882,6 +48869,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -5901,11 +48889,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -5913,7 +48917,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -5938,31 +48944,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -5972,6 +48989,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -5996,10 +49014,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -6007,7 +49041,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -6032,43 +49068,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "NormalSrcOver" composition equation. + /// A pixel blender that implements the "NormalDestOver" composition equation. /// - public class NormalSrcOver : PixelBlender + public class NormalDestOver : PixelBlender { /// /// Gets the static instance of this blender. /// - public static NormalSrcOver Instance { get; } = new NormalSrcOver(); + public static NormalDestOver Instance { get; } = new NormalDestOver(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -6088,7 +49135,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -6248,7 +49295,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -6284,14 +49331,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -6342,7 +49389,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -6377,37 +49424,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "MultiplySrcOver" composition equation. - /// - public class MultiplySrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcOver Instance { get; } = new MultiplySrcOver(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -6419,14 +49449,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -6434,7 +49482,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -6484,18 +49546,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -6503,7 +49584,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.NormalDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.NormalDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -6552,6 +49648,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -6571,11 +49668,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -6583,7 +49696,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -6608,31 +49723,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -6642,6 +49768,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -6666,10 +49793,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -6677,7 +49820,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -6702,43 +49847,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "AddSrcOver" composition equation. + /// A pixel blender that implements the "MultiplyDestOver" composition equation. /// - public class AddSrcOver : PixelBlender + public class MultiplyDestOver : PixelBlender { /// /// Gets the static instance of this blender. /// - public static AddSrcOver Instance { get; } = new AddSrcOver(); + public static MultiplyDestOver Instance { get; } = new MultiplyDestOver(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -6758,7 +49914,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -6918,7 +50074,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -6954,14 +50110,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -7012,7 +50168,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -7047,37 +50203,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "SubtractSrcOver" composition equation. - /// - public class SubtractSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcOver Instance { get; } = new SubtractSrcOver(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -7089,14 +50228,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -7104,7 +50261,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -7154,18 +50325,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -7173,7 +50363,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.MultiplyDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.MultiplyDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -7222,6 +50427,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -7241,11 +50447,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -7253,7 +50475,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -7278,31 +50502,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -7312,6 +50547,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -7336,10 +50572,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -7347,7 +50599,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -7372,43 +50626,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "ScreenSrcOver" composition equation. + /// A pixel blender that implements the "AddDestOver" composition equation. /// - public class ScreenSrcOver : PixelBlender + public class AddDestOver : PixelBlender { /// /// Gets the static instance of this blender. /// - public static ScreenSrcOver Instance { get; } = new ScreenSrcOver(); + public static AddDestOver Instance { get; } = new AddDestOver(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -7428,7 +50693,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -7588,7 +50853,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -7624,14 +50889,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -7682,7 +50947,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -7717,37 +50982,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "DarkenSrcOver" composition equation. - /// - public class DarkenSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcOver Instance { get; } = new DarkenSrcOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -7759,14 +51007,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -7774,7 +51040,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -7824,18 +51104,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -7843,7 +51142,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.AddDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.AddDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -7892,6 +51206,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -7911,11 +51226,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -7923,7 +51254,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -7948,31 +51281,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -7982,6 +51326,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -8006,10 +51351,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -8017,7 +51378,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -8042,43 +51405,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "LightenSrcOver" composition equation. + /// A pixel blender that implements the "SubtractDestOver" composition equation. /// - public class LightenSrcOver : PixelBlender + public class SubtractDestOver : PixelBlender { /// /// Gets the static instance of this blender. /// - public static LightenSrcOver Instance { get; } = new LightenSrcOver(); + public static SubtractDestOver Instance { get; } = new SubtractDestOver(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -8098,7 +51472,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -8258,7 +51632,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -8294,14 +51668,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -8352,7 +51726,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -8387,37 +51761,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "OverlaySrcOver" composition equation. - /// - public class OverlaySrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcOver Instance { get; } = new OverlaySrcOver(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -8429,14 +51786,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -8444,7 +51819,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -8494,18 +51883,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -8513,7 +51921,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.SubtractDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.SubtractDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -8562,6 +51985,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -8581,11 +52005,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -8593,7 +52033,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -8618,31 +52060,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -8652,6 +52105,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -8676,10 +52130,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -8687,7 +52157,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -8712,43 +52184,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "HardLightSrcOver" composition equation. + /// A pixel blender that implements the "ScreenDestOver" composition equation. /// - public class HardLightSrcOver : PixelBlender + public class ScreenDestOver : PixelBlender { /// /// Gets the static instance of this blender. /// - public static HardLightSrcOver Instance { get; } = new HardLightSrcOver(); + public static ScreenDestOver Instance { get; } = new ScreenDestOver(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -8768,7 +52251,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -8928,7 +52411,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -8964,14 +52447,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -9022,7 +52505,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -9057,37 +52540,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "NormalSrcIn" composition equation. - /// - public class NormalSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcIn Instance { get; } = new NormalSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -9099,14 +52565,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -9114,7 +52598,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -9164,18 +52662,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -9183,7 +52700,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -9232,6 +52764,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -9251,11 +52784,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -9263,7 +52812,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -9288,31 +52839,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -9322,6 +52884,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -9346,10 +52909,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -9357,7 +52936,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -9382,43 +52963,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "MultiplySrcIn" composition equation. + /// A pixel blender that implements the "DarkenDestOver" composition equation. /// - public class MultiplySrcIn : PixelBlender + public class DarkenDestOver : PixelBlender { /// /// Gets the static instance of this blender. /// - public static MultiplySrcIn Instance { get; } = new MultiplySrcIn(); + public static DarkenDestOver Instance { get; } = new DarkenDestOver(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -9438,7 +53030,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -9598,7 +53190,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -9634,14 +53226,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -9692,7 +53284,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -9727,37 +53319,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "AddSrcIn" composition equation. - /// - public class AddSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrcIn Instance { get; } = new AddSrcIn(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -9769,14 +53344,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -9784,7 +53377,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -9834,18 +53441,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -9853,7 +53479,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.DarkenDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.DarkenDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -9902,6 +53543,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -9921,11 +53563,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -9933,7 +53591,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -9958,31 +53618,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -9992,6 +53663,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -10016,10 +53688,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -10027,7 +53715,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -10052,43 +53742,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "SubtractSrcIn" composition equation. + /// A pixel blender that implements the "LightenDestOver" composition equation. /// - public class SubtractSrcIn : PixelBlender + public class LightenDestOver : PixelBlender { /// /// Gets the static instance of this blender. /// - public static SubtractSrcIn Instance { get; } = new SubtractSrcIn(); + public static LightenDestOver Instance { get; } = new LightenDestOver(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -10108,7 +53809,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -10268,7 +53969,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -10304,14 +54005,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -10362,7 +54063,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -10397,37 +54098,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "ScreenSrcIn" composition equation. - /// - public class ScreenSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcIn Instance { get; } = new ScreenSrcIn(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -10439,14 +54123,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -10454,7 +54156,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -10504,18 +54220,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -10523,7 +54258,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.LightenDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.LightenDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -10572,6 +54322,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -10591,11 +54342,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -10603,7 +54370,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -10628,31 +54397,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -10662,6 +54442,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -10686,10 +54467,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -10697,7 +54494,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -10722,43 +54521,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "DarkenSrcIn" composition equation. + /// A pixel blender that implements the "OverlayDestOver" composition equation. /// - public class DarkenSrcIn : PixelBlender + public class OverlayDestOver : PixelBlender { /// /// Gets the static instance of this blender. /// - public static DarkenSrcIn Instance { get; } = new DarkenSrcIn(); + public static OverlayDestOver Instance { get; } = new OverlayDestOver(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -10778,7 +54588,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -10938,7 +54748,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -10974,14 +54784,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -11032,7 +54842,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -11067,37 +54877,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "LightenSrcIn" composition equation. - /// - public class LightenSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcIn Instance { get; } = new LightenSrcIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -11109,14 +54902,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -11124,7 +54935,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -11174,18 +54999,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -11193,7 +55037,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.OverlayDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.OverlayDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -11242,6 +55101,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -11261,11 +55121,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -11273,7 +55149,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -11298,31 +55176,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -11332,6 +55221,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -11356,10 +55246,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -11367,7 +55273,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -11392,43 +55300,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "OverlaySrcIn" composition equation. + /// A pixel blender that implements the "HardLightDestOver" composition equation. /// - public class OverlaySrcIn : PixelBlender + public class HardLightDestOver : PixelBlender { /// /// Gets the static instance of this blender. /// - public static OverlaySrcIn Instance { get; } = new OverlaySrcIn(); + public static HardLightDestOver Instance { get; } = new HardLightDestOver(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -11448,7 +55367,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -11608,7 +55527,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -11644,14 +55563,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -11702,7 +55621,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -11737,37 +55656,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "HardLightSrcIn" composition equation. - /// - public class HardLightSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcIn Instance { get; } = new HardLightSrcIn(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -11779,14 +55681,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -11794,7 +55714,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -11844,18 +55778,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -11863,7 +55816,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.HardLightDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.HardLightDestOver(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -11912,6 +55880,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -11931,11 +55900,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -11943,7 +55928,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -11968,31 +55955,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -12002,6 +56000,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -12026,10 +56025,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -12037,7 +56052,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -12062,43 +56079,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "NormalSrcOut" composition equation. + /// A pixel blender that implements the "NormalDestIn" composition equation. /// - public class NormalSrcOut : PixelBlender + public class NormalDestIn : PixelBlender { /// /// Gets the static instance of this blender. /// - public static NormalSrcOut Instance { get; } = new NormalSrcOut(); + public static NormalDestIn Instance { get; } = new NormalDestIn(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -12118,7 +56146,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -12278,7 +56306,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -12314,14 +56342,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -12372,7 +56400,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -12407,37 +56435,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "MultiplySrcOut" composition equation. - /// - public class MultiplySrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcOut Instance { get; } = new MultiplySrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -12449,14 +56460,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -12464,7 +56493,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -12514,18 +56557,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -12533,7 +56595,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.NormalDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.NormalDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -12582,6 +56659,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -12601,11 +56679,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -12613,7 +56707,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -12638,31 +56734,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -12672,6 +56779,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -12696,10 +56804,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -12707,7 +56831,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -12732,43 +56858,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "AddSrcOut" composition equation. + /// A pixel blender that implements the "MultiplyDestIn" composition equation. /// - public class AddSrcOut : PixelBlender + public class MultiplyDestIn : PixelBlender { /// /// Gets the static instance of this blender. /// - public static AddSrcOut Instance { get; } = new AddSrcOut(); + public static MultiplyDestIn Instance { get; } = new MultiplyDestIn(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -12788,7 +56925,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -12948,7 +57085,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -12984,14 +57121,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -13042,7 +57179,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -13077,37 +57214,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "SubtractSrcOut" composition equation. - /// - public class SubtractSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcOut Instance { get; } = new SubtractSrcOut(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -13119,14 +57239,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -13134,7 +57272,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -13184,18 +57336,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -13203,7 +57374,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.MultiplyDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.MultiplyDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -13252,6 +57438,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -13271,11 +57458,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -13283,7 +57486,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -13308,31 +57513,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -13342,6 +57558,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -13366,10 +57583,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -13377,7 +57610,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -13402,43 +57637,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "ScreenSrcOut" composition equation. + /// A pixel blender that implements the "AddDestIn" composition equation. /// - public class ScreenSrcOut : PixelBlender + public class AddDestIn : PixelBlender { /// /// Gets the static instance of this blender. /// - public static ScreenSrcOut Instance { get; } = new ScreenSrcOut(); + public static AddDestIn Instance { get; } = new AddDestIn(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -13458,7 +57704,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -13618,7 +57864,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -13654,14 +57900,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -13712,7 +57958,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -13747,37 +57993,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "DarkenSrcOut" composition equation. - /// - public class DarkenSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcOut Instance { get; } = new DarkenSrcOut(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -13789,14 +58018,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -13804,7 +58051,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -13854,18 +58115,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -13873,7 +58153,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.AddDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.AddDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -13922,6 +58217,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -13941,11 +58237,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -13953,7 +58265,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -13978,31 +58292,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -14012,6 +58337,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -14036,10 +58362,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -14047,7 +58389,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -14072,43 +58416,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "LightenSrcOut" composition equation. + /// A pixel blender that implements the "SubtractDestIn" composition equation. /// - public class LightenSrcOut : PixelBlender + public class SubtractDestIn : PixelBlender { /// /// Gets the static instance of this blender. /// - public static LightenSrcOut Instance { get; } = new LightenSrcOut(); + public static SubtractDestIn Instance { get; } = new SubtractDestIn(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -14128,7 +58483,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -14288,7 +58643,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -14324,14 +58679,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -14382,7 +58737,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -14417,37 +58772,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "OverlaySrcOut" composition equation. - /// - public class OverlaySrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcOut Instance { get; } = new OverlaySrcOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -14459,14 +58797,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -14474,7 +58830,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -14524,18 +58894,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -14543,7 +58932,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.SubtractDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.SubtractDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -14592,6 +58996,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -14611,11 +59016,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -14623,7 +59044,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -14648,31 +59071,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -14682,6 +59116,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -14706,10 +59141,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -14717,7 +59168,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -14742,43 +59195,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "HardLightSrcOut" composition equation. + /// A pixel blender that implements the "ScreenDestIn" composition equation. /// - public class HardLightSrcOut : PixelBlender + public class ScreenDestIn : PixelBlender { /// /// Gets the static instance of this blender. /// - public static HardLightSrcOut Instance { get; } = new HardLightSrcOut(); + public static ScreenDestIn Instance { get; } = new ScreenDestIn(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -14798,7 +59262,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -14958,7 +59422,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -14994,14 +59458,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -15052,7 +59516,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -15087,37 +59551,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "NormalDest" composition equation. - /// - public class NormalDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDest Instance { get; } = new NormalDest(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -15129,14 +59576,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -15144,7 +59609,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -15194,18 +59673,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -15213,7 +59711,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDest(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -15262,6 +59775,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -15281,11 +59795,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -15293,7 +59823,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -15318,31 +59850,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -15352,6 +59895,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -15376,10 +59920,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -15387,7 +59947,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -15412,43 +59974,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "MultiplyDest" composition equation. + /// A pixel blender that implements the "DarkenDestIn" composition equation. /// - public class MultiplyDest : PixelBlender + public class DarkenDestIn : PixelBlender { /// /// Gets the static instance of this blender. /// - public static MultiplyDest Instance { get; } = new MultiplyDest(); + public static DarkenDestIn Instance { get; } = new DarkenDestIn(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -15468,7 +60041,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -15628,7 +60201,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -15664,14 +60237,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -15722,7 +60295,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -15757,37 +60330,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "AddDest" composition equation. - /// - public class AddDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDest Instance { get; } = new AddDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.AddDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -15799,14 +60355,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -15814,7 +60388,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -15864,18 +60452,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -15883,7 +60490,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDest(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.DarkenDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDest(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.DarkenDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -15932,6 +60554,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -15951,11 +60574,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -15963,7 +60602,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -15988,31 +60629,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -16022,6 +60674,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -16046,10 +60699,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -16057,7 +60726,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -16082,43 +60753,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "SubtractDest" composition equation. + /// A pixel blender that implements the "LightenDestIn" composition equation. /// - public class SubtractDest : PixelBlender + public class LightenDestIn : PixelBlender { /// /// Gets the static instance of this blender. /// - public static SubtractDest Instance { get; } = new SubtractDest(); + public static LightenDestIn Instance { get; } = new LightenDestIn(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -16138,7 +60820,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -16298,7 +60980,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -16334,14 +61016,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -16392,7 +61074,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -16427,37 +61109,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "ScreenDest" composition equation. - /// - public class ScreenDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDest Instance { get; } = new ScreenDest(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -16469,14 +61134,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -16484,7 +61167,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -16534,18 +61231,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -16553,7 +61269,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.LightenDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.LightenDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -16602,6 +61333,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -16621,11 +61353,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -16633,7 +61381,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -16658,31 +61408,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -16692,6 +61453,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -16716,10 +61478,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -16727,7 +61505,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -16752,43 +61532,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "DarkenDest" composition equation. + /// A pixel blender that implements the "OverlayDestIn" composition equation. /// - public class DarkenDest : PixelBlender + public class OverlayDestIn : PixelBlender { /// /// Gets the static instance of this blender. /// - public static DarkenDest Instance { get; } = new DarkenDest(); + public static OverlayDestIn Instance { get; } = new OverlayDestIn(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -16808,7 +61599,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -16968,7 +61759,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -17004,14 +61795,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -17062,7 +61853,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -17097,37 +61888,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "LightenDest" composition equation. - /// - public class LightenDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDest Instance { get; } = new LightenDest(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -17139,14 +61913,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -17154,7 +61946,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -17204,18 +62010,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -17223,7 +62048,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDest(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.OverlayDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.OverlayDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -17272,6 +62112,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -17291,11 +62132,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -17303,7 +62160,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -17328,31 +62187,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -17362,6 +62232,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -17386,10 +62257,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -17397,7 +62284,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -17422,43 +62311,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "OverlayDest" composition equation. + /// A pixel blender that implements the "HardLightDestIn" composition equation. /// - public class OverlayDest : PixelBlender + public class HardLightDestIn : PixelBlender { /// /// Gets the static instance of this blender. /// - public static OverlayDest Instance { get; } = new OverlayDest(); + public static HardLightDestIn Instance { get; } = new HardLightDestIn(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -17478,7 +62378,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -17638,7 +62538,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -17674,14 +62574,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -17732,7 +62632,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -17767,37 +62667,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "HardLightDest" composition equation. - /// - public class HardLightDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDest Instance { get; } = new HardLightDest(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -17809,14 +62692,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -17824,7 +62725,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -17874,18 +62789,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -17893,7 +62827,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.HardLightDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.HardLightDestIn(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -17942,6 +62891,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -17961,11 +62911,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -17973,7 +62939,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -17998,31 +62966,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -18032,6 +63011,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -18056,10 +63036,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -18067,7 +63063,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -18092,43 +63090,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "NormalDestAtop" composition equation. + /// A pixel blender that implements the "NormalDestOut" composition equation. /// - public class NormalDestAtop : PixelBlender + public class NormalDestOut : PixelBlender { /// /// Gets the static instance of this blender. /// - public static NormalDestAtop Instance { get; } = new NormalDestAtop(); + public static NormalDestOut Instance { get; } = new NormalDestOut(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -18148,7 +63157,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -18308,7 +63317,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -18344,14 +63353,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -18402,7 +63411,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -18437,37 +63446,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "MultiplyDestAtop" composition equation. - /// - public class MultiplyDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestAtop Instance { get; } = new MultiplyDestAtop(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -18479,14 +63471,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -18494,7 +63504,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -18544,18 +63568,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -18563,7 +63606,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.NormalDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.NormalDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -18612,6 +63670,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -18631,11 +63690,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -18643,7 +63718,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -18668,31 +63745,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -18702,6 +63790,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -18726,10 +63815,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -18737,7 +63842,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -18762,43 +63869,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "AddDestAtop" composition equation. + /// A pixel blender that implements the "MultiplyDestOut" composition equation. /// - public class AddDestAtop : PixelBlender + public class MultiplyDestOut : PixelBlender { /// /// Gets the static instance of this blender. /// - public static AddDestAtop Instance { get; } = new AddDestAtop(); + public static MultiplyDestOut Instance { get; } = new MultiplyDestOut(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -18818,7 +63936,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -18978,7 +64096,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -19014,14 +64132,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -19072,7 +64190,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -19107,37 +64225,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "SubtractDestAtop" composition equation. - /// - public class SubtractDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestAtop Instance { get; } = new SubtractDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -19149,14 +64250,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -19164,7 +64283,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -19214,18 +64347,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -19233,7 +64385,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.MultiplyDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.MultiplyDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -19282,6 +64449,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -19301,11 +64469,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -19313,7 +64497,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -19338,31 +64524,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -19372,6 +64569,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -19396,10 +64594,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -19407,7 +64621,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -19432,43 +64648,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "ScreenDestAtop" composition equation. + /// A pixel blender that implements the "AddDestOut" composition equation. /// - public class ScreenDestAtop : PixelBlender + public class AddDestOut : PixelBlender { /// /// Gets the static instance of this blender. /// - public static ScreenDestAtop Instance { get; } = new ScreenDestAtop(); + public static AddDestOut Instance { get; } = new AddDestOut(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -19488,7 +64715,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -19648,7 +64875,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -19684,14 +64911,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -19742,7 +64969,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -19777,37 +65004,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "DarkenDestAtop" composition equation. - /// - public class DarkenDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestAtop Instance { get; } = new DarkenDestAtop(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -19819,14 +65029,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -19834,7 +65062,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -19884,18 +65126,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -19903,7 +65164,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.AddDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.AddDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -19952,6 +65228,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -19971,11 +65248,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -19983,7 +65276,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -20008,31 +65303,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -20042,6 +65348,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -20066,10 +65373,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -20077,7 +65400,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -20102,43 +65427,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "LightenDestAtop" composition equation. + /// A pixel blender that implements the "SubtractDestOut" composition equation. /// - public class LightenDestAtop : PixelBlender + public class SubtractDestOut : PixelBlender { /// /// Gets the static instance of this blender. /// - public static LightenDestAtop Instance { get; } = new LightenDestAtop(); + public static SubtractDestOut Instance { get; } = new SubtractDestOut(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -20158,7 +65494,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -20318,7 +65654,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -20354,14 +65690,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -20412,7 +65748,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -20447,37 +65783,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "OverlayDestAtop" composition equation. - /// - public class OverlayDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestAtop Instance { get; } = new OverlayDestAtop(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -20489,14 +65808,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -20504,7 +65841,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -20554,18 +65905,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -20573,7 +65943,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.SubtractDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.SubtractDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -20622,6 +66007,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -20641,11 +66027,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -20653,7 +66055,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -20678,31 +66082,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -20712,6 +66127,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -20736,10 +66152,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -20747,7 +66179,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -20772,43 +66206,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "HardLightDestAtop" composition equation. + /// A pixel blender that implements the "ScreenDestOut" composition equation. /// - public class HardLightDestAtop : PixelBlender + public class ScreenDestOut : PixelBlender { /// /// Gets the static instance of this blender. /// - public static HardLightDestAtop Instance { get; } = new HardLightDestAtop(); + public static ScreenDestOut Instance { get; } = new ScreenDestOut(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -20828,7 +66273,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -20988,7 +66433,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -21024,14 +66469,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -21082,7 +66527,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -21117,37 +66562,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "NormalDestOver" composition equation. - /// - public class NormalDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestOver Instance { get; } = new NormalDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -21159,14 +66587,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -21174,7 +66620,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -21224,18 +66684,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -21243,7 +66722,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -21292,6 +66786,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -21311,11 +66806,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -21323,7 +66834,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -21348,31 +66861,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -21382,6 +66906,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -21406,10 +66931,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -21417,7 +66958,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -21442,43 +66985,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "MultiplyDestOver" composition equation. + /// A pixel blender that implements the "DarkenDestOut" composition equation. /// - public class MultiplyDestOver : PixelBlender + public class DarkenDestOut : PixelBlender { /// /// Gets the static instance of this blender. /// - public static MultiplyDestOver Instance { get; } = new MultiplyDestOver(); + public static DarkenDestOut Instance { get; } = new DarkenDestOut(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -21498,7 +67052,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -21658,7 +67212,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -21694,14 +67248,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -21752,7 +67306,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -21787,37 +67341,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "AddDestOver" composition equation. - /// - public class AddDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestOver Instance { get; } = new AddDestOver(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -21829,14 +67366,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -21844,7 +67399,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -21894,18 +67463,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -21913,7 +67501,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.DarkenDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.DarkenDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -21962,6 +67565,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -21981,11 +67585,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -21993,7 +67613,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -22018,31 +67640,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -22052,6 +67685,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -22076,10 +67710,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -22087,7 +67737,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -22112,43 +67764,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "SubtractDestOver" composition equation. + /// A pixel blender that implements the "LightenDestOut" composition equation. /// - public class SubtractDestOver : PixelBlender + public class LightenDestOut : PixelBlender { /// /// Gets the static instance of this blender. /// - public static SubtractDestOver Instance { get; } = new SubtractDestOver(); + public static LightenDestOut Instance { get; } = new LightenDestOut(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -22168,7 +67831,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -22328,7 +67991,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -22364,14 +68027,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -22422,7 +68085,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -22457,37 +68120,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "ScreenDestOver" composition equation. - /// - public class ScreenDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestOver Instance { get; } = new ScreenDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -22499,14 +68145,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -22514,7 +68178,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -22564,18 +68242,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -22583,7 +68280,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.LightenDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.LightenDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -22632,6 +68344,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -22651,11 +68364,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -22663,7 +68392,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -22688,31 +68419,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -22722,6 +68464,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -22746,10 +68489,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -22757,7 +68516,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -22782,43 +68543,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "DarkenDestOver" composition equation. + /// A pixel blender that implements the "OverlayDestOut" composition equation. /// - public class DarkenDestOver : PixelBlender + public class OverlayDestOut : PixelBlender { /// /// Gets the static instance of this blender. /// - public static DarkenDestOver Instance { get; } = new DarkenDestOver(); + public static OverlayDestOut Instance { get; } = new OverlayDestOut(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -22838,7 +68610,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -22998,7 +68770,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -23034,14 +68806,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -23092,7 +68864,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -23127,37 +68899,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "LightenDestOver" composition equation. - /// - public class LightenDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestOver Instance { get; } = new LightenDestOver(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -23169,14 +68924,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -23184,7 +68957,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -23234,18 +69021,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -23253,7 +69059,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.OverlayDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.OverlayDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -23302,6 +69123,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -23321,11 +69143,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -23333,7 +69171,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -23358,31 +69198,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -23392,6 +69243,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -23416,10 +69268,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -23427,7 +69295,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -23452,43 +69322,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "OverlayDestOver" composition equation. + /// A pixel blender that implements the "HardLightDestOut" composition equation. /// - public class OverlayDestOver : PixelBlender + public class HardLightDestOut : PixelBlender { /// /// Gets the static instance of this blender. /// - public static OverlayDestOver Instance { get; } = new OverlayDestOver(); + public static HardLightDestOut Instance { get; } = new HardLightDestOut(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -23508,7 +69389,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -23668,7 +69549,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -23704,14 +69585,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -23762,7 +69643,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -23797,37 +69678,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "HardLightDestOver" composition equation. - /// - public class HardLightDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestOver Instance { get; } = new HardLightDestOver(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -23839,14 +69703,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -23854,7 +69736,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -23904,18 +69800,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -23923,7 +69838,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.HardLightDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.HardLightDestOut(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -23972,6 +69902,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -23991,11 +69922,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -24003,7 +69950,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -24028,31 +69977,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -24062,6 +70022,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -24086,10 +70047,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -24097,7 +70074,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -24122,43 +70101,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "NormalDestIn" composition equation. + /// A pixel blender that implements the "NormalClear" composition equation. /// - public class NormalDestIn : PixelBlender + public class NormalClear : PixelBlender { /// /// Gets the static instance of this blender. /// - public static NormalDestIn Instance { get; } = new NormalDestIn(); + public static NormalClear Instance { get; } = new NormalClear(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -24178,7 +70168,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -24338,7 +70328,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -24374,14 +70364,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -24432,7 +70422,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -24467,37 +70457,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "MultiplyDestIn" composition equation. - /// - public class MultiplyDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestIn Instance { get; } = new MultiplyDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -24509,14 +70482,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -24524,7 +70515,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.NormalClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.NormalClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -24574,18 +70579,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -24593,7 +70617,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.NormalClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.NormalClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -24642,6 +70681,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -24661,11 +70701,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -24673,7 +70729,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -24698,31 +70756,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -24732,6 +70801,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -24756,10 +70826,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -24767,7 +70853,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -24792,43 +70880,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "AddDestIn" composition equation. + /// A pixel blender that implements the "MultiplyClear" composition equation. /// - public class AddDestIn : PixelBlender + public class MultiplyClear : PixelBlender { /// /// Gets the static instance of this blender. /// - public static AddDestIn Instance { get; } = new AddDestIn(); + public static MultiplyClear Instance { get; } = new MultiplyClear(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -24848,7 +70947,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -25008,7 +71107,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -25044,14 +71143,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -25102,7 +71201,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -25137,37 +71236,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "SubtractDestIn" composition equation. - /// - public class SubtractDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestIn Instance { get; } = new SubtractDestIn(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -25179,14 +71261,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -25194,7 +71294,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -25244,18 +71358,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -25263,7 +71396,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.MultiplyClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.MultiplyClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -25312,6 +71460,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -25331,11 +71480,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -25343,7 +71508,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -25368,31 +71535,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -25402,6 +71580,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -25426,10 +71605,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -25437,7 +71632,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -25462,43 +71659,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "ScreenDestIn" composition equation. + /// A pixel blender that implements the "AddClear" composition equation. /// - public class ScreenDestIn : PixelBlender + public class AddClear : PixelBlender { /// /// Gets the static instance of this blender. /// - public static ScreenDestIn Instance { get; } = new ScreenDestIn(); + public static AddClear Instance { get; } = new AddClear(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.AddClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -25518,7 +71726,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -25678,7 +71886,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -25714,14 +71922,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -25772,7 +71980,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -25807,37 +72015,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "DarkenDestIn" composition equation. - /// - public class DarkenDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestIn Instance { get; } = new DarkenDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -25849,14 +72040,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -25864,7 +72073,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.AddClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.AddClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -25914,18 +72137,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -25933,7 +72175,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.AddClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.AddClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -25982,6 +72239,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -26001,11 +72259,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -26013,7 +72287,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -26038,31 +72314,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -26072,6 +72359,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -26096,10 +72384,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -26107,7 +72411,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -26132,43 +72438,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "LightenDestIn" composition equation. + /// A pixel blender that implements the "SubtractClear" composition equation. /// - public class LightenDestIn : PixelBlender + public class SubtractClear : PixelBlender { /// /// Gets the static instance of this blender. /// - public static LightenDestIn Instance { get; } = new LightenDestIn(); + public static SubtractClear Instance { get; } = new SubtractClear(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -26188,7 +72505,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -26348,7 +72665,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -26384,14 +72701,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -26442,7 +72759,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -26477,37 +72794,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "OverlayDestIn" composition equation. - /// - public class OverlayDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestIn Instance { get; } = new OverlayDestIn(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -26519,14 +72819,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -26534,7 +72852,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -26584,18 +72916,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -26603,7 +72954,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.SubtractClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.SubtractClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -26652,6 +73018,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -26671,11 +73038,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -26683,7 +73066,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -26708,31 +73093,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -26742,6 +73138,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -26766,10 +73163,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -26777,7 +73190,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -26802,43 +73217,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "HardLightDestIn" composition equation. + /// A pixel blender that implements the "ScreenClear" composition equation. /// - public class HardLightDestIn : PixelBlender + public class ScreenClear : PixelBlender { /// /// Gets the static instance of this blender. /// - public static HardLightDestIn Instance { get; } = new HardLightDestIn(); + public static ScreenClear Instance { get; } = new ScreenClear(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -26858,7 +73284,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -27018,7 +73444,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -27054,14 +73480,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -27112,7 +73538,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -27147,37 +73573,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "NormalDestOut" composition equation. - /// - public class NormalDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestOut Instance { get; } = new NormalDestOut(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -27189,14 +73598,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -27204,7 +73631,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -27254,18 +73695,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -27273,7 +73733,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -27322,6 +73797,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -27341,11 +73817,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -27353,7 +73845,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -27378,31 +73872,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -27412,6 +73917,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -27436,10 +73942,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -27447,7 +73969,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -27472,43 +73996,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "MultiplyDestOut" composition equation. + /// A pixel blender that implements the "DarkenClear" composition equation. /// - public class MultiplyDestOut : PixelBlender + public class DarkenClear : PixelBlender { /// /// Gets the static instance of this blender. /// - public static MultiplyDestOut Instance { get; } = new MultiplyDestOut(); + public static DarkenClear Instance { get; } = new DarkenClear(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -27528,7 +74063,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -27688,7 +74223,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -27724,14 +74259,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -27782,7 +74317,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -27817,37 +74352,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "AddDestOut" composition equation. - /// - public class AddDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestOut Instance { get; } = new AddDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -27859,14 +74377,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -27874,7 +74410,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -27924,18 +74474,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -27943,7 +74512,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.DarkenClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.DarkenClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -27992,6 +74576,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -28011,11 +74596,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -28023,7 +74624,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -28048,31 +74651,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -28082,6 +74696,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -28106,10 +74721,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -28117,7 +74748,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -28142,43 +74775,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "SubtractDestOut" composition equation. + /// A pixel blender that implements the "LightenClear" composition equation. /// - public class SubtractDestOut : PixelBlender + public class LightenClear : PixelBlender { /// /// Gets the static instance of this blender. /// - public static SubtractDestOut Instance { get; } = new SubtractDestOut(); + public static LightenClear Instance { get; } = new LightenClear(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -28198,7 +74842,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -28358,7 +75002,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -28394,14 +75038,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -28452,7 +75096,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -28487,37 +75131,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "ScreenDestOut" composition equation. - /// - public class ScreenDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestOut Instance { get; } = new ScreenDestOut(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -28529,14 +75156,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -28544,7 +75189,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.LightenClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.LightenClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -28594,18 +75253,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -28613,7 +75291,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.LightenClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.LightenClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -28662,6 +75355,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -28681,11 +75375,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -28693,7 +75403,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -28718,31 +75430,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -28752,6 +75475,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -28776,10 +75500,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -28787,7 +75527,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -28812,43 +75554,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "DarkenDestOut" composition equation. + /// A pixel blender that implements the "OverlayClear" composition equation. /// - public class DarkenDestOut : PixelBlender + public class OverlayClear : PixelBlender { /// /// Gets the static instance of this blender. /// - public static DarkenDestOut Instance { get; } = new DarkenDestOut(); + public static OverlayClear Instance { get; } = new OverlayClear(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -28868,7 +75621,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -29028,7 +75781,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -29064,14 +75817,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -29122,7 +75875,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -29157,37 +75910,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "LightenDestOut" composition equation. - /// - public class LightenDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestOut Instance { get; } = new LightenDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -29199,14 +75935,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -29214,7 +75968,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -29264,18 +76032,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -29283,7 +76070,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.OverlayClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.OverlayClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -29332,6 +76134,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -29351,11 +76154,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -29363,7 +76182,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -29388,31 +76209,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -29422,6 +76254,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -29446,10 +76279,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -29457,7 +76306,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -29482,43 +76333,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "OverlayDestOut" composition equation. + /// A pixel blender that implements the "HardLightClear" composition equation. /// - public class OverlayDestOut : PixelBlender + public class HardLightClear : PixelBlender { /// /// Gets the static instance of this blender. /// - public static OverlayDestOut Instance { get; } = new OverlayDestOut(); + public static HardLightClear Instance { get; } = new HardLightClear(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -29538,7 +76400,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -29698,7 +76560,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -29734,14 +76596,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -29792,7 +76654,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -29827,37 +76689,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "HardLightDestOut" composition equation. - /// - public class HardLightDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestOut Instance { get; } = new HardLightDestOut(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -29869,14 +76714,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -29884,7 +76747,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -29934,18 +76811,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -29953,7 +76849,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.HardLightClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.HardLightClear(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -30002,6 +76913,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -30021,11 +76933,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -30033,7 +76961,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -30058,31 +76988,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -30092,6 +77033,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -30116,10 +77058,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -30127,7 +77085,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -30152,43 +77112,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "NormalClear" composition equation. + /// A pixel blender that implements the "NormalXor" composition equation. /// - public class NormalClear : PixelBlender + public class NormalXor : PixelBlender { /// /// Gets the static instance of this blender. /// - public static NormalClear Instance { get; } = new NormalClear(); + public static NormalXor Instance { get; } = new NormalXor(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.NormalClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.NormalXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -30208,7 +77179,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -30368,7 +77339,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -30404,14 +77375,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -30462,7 +77433,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -30497,37 +77468,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "MultiplyClear" composition equation. - /// - public class MultiplyClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyClear Instance { get; } = new MultiplyClear(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -30539,14 +77493,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -30554,7 +77526,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.NormalXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.NormalXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -30604,18 +77590,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -30623,7 +77628,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.NormalXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.NormalXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -30672,6 +77692,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -30691,11 +77712,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -30703,7 +77740,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -30728,31 +77767,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -30762,6 +77812,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -30786,10 +77837,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -30797,7 +77864,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -30822,43 +77891,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.NormalXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "AddClear" composition equation. + /// A pixel blender that implements the "MultiplyXor" composition equation. /// - public class AddClear : PixelBlender + public class MultiplyXor : PixelBlender { /// /// Gets the static instance of this blender. /// - public static AddClear Instance { get; } = new AddClear(); + public static MultiplyXor Instance { get; } = new MultiplyXor(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.AddClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -30878,7 +77958,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -31038,7 +78118,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -31074,14 +78154,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -31132,7 +78212,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -31167,37 +78247,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "SubtractClear" composition equation. - /// - public class SubtractClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractClear Instance { get; } = new SubtractClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -31209,14 +78272,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -31224,7 +78305,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -31274,18 +78369,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -31293,7 +78407,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.MultiplyXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.MultiplyXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -31342,6 +78471,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -31361,11 +78491,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -31373,7 +78519,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -31398,31 +78546,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -31432,6 +78591,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -31456,10 +78616,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -31467,7 +78643,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -31492,43 +78670,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.MultiplyXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "ScreenClear" composition equation. + /// A pixel blender that implements the "AddXor" composition equation. /// - public class ScreenClear : PixelBlender + public class AddXor : PixelBlender { /// /// Gets the static instance of this blender. /// - public static ScreenClear Instance { get; } = new ScreenClear(); + public static AddXor Instance { get; } = new AddXor(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.AddXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -31548,7 +78737,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -31708,7 +78897,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -31744,14 +78933,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -31802,7 +78991,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -31837,37 +79026,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "DarkenClear" composition equation. - /// - public class DarkenClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenClear Instance { get; } = new DarkenClear(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -31879,14 +79051,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -31894,7 +79084,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.AddXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.AddXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -31944,18 +79148,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -31963,7 +79186,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.AddXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.AddXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -32012,6 +79250,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -32031,11 +79270,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -32043,7 +79298,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -32068,31 +79325,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -32102,6 +79370,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -32126,10 +79395,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -32137,7 +79422,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -32162,43 +79449,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.AddXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "LightenClear" composition equation. + /// A pixel blender that implements the "SubtractXor" composition equation. /// - public class LightenClear : PixelBlender + public class SubtractXor : PixelBlender { /// /// Gets the static instance of this blender. /// - public static LightenClear Instance { get; } = new LightenClear(); + public static SubtractXor Instance { get; } = new SubtractXor(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.LightenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -32218,7 +79516,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -32378,7 +79676,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -32414,14 +79712,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -32472,7 +79770,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -32507,37 +79805,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "OverlayClear" composition equation. - /// - public class OverlayClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayClear Instance { get; } = new OverlayClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -32549,14 +79830,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -32564,7 +79863,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -32614,18 +79927,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -32633,7 +79965,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.SubtractXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.SubtractXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -32682,6 +80029,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -32701,11 +80049,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -32713,7 +80077,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -32738,31 +80104,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -32772,6 +80149,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -32796,10 +80174,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -32807,7 +80201,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -32832,43 +80228,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.SubtractXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "HardLightClear" composition equation. + /// A pixel blender that implements the "ScreenXor" composition equation. /// - public class HardLightClear : PixelBlender + public class ScreenXor : PixelBlender { /// /// Gets the static instance of this blender. /// - public static HardLightClear Instance { get; } = new HardLightClear(); + public static ScreenXor Instance { get; } = new ScreenXor(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -32888,7 +80295,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -33048,7 +80455,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -33084,14 +80491,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -33142,7 +80549,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -33177,37 +80584,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "NormalXor" composition equation. - /// - public class NormalXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalXor Instance { get; } = new NormalXor(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.NormalXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -33219,14 +80609,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -33234,7 +80642,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -33284,18 +80706,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -33303,7 +80744,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalXor(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.ScreenXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -33352,6 +80808,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -33371,11 +80828,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -33383,7 +80856,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -33408,31 +80883,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -33442,6 +80928,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -33466,10 +80953,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -33477,7 +80980,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -33502,43 +81007,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.ScreenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "MultiplyXor" composition equation. + /// A pixel blender that implements the "DarkenXor" composition equation. /// - public class MultiplyXor : PixelBlender + public class DarkenXor : PixelBlender { /// /// Gets the static instance of this blender. /// - public static MultiplyXor Instance { get; } = new MultiplyXor(); + public static DarkenXor Instance { get; } = new DarkenXor(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -33558,7 +81074,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -33718,7 +81234,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -33754,14 +81270,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -33812,7 +81328,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -33847,37 +81363,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "AddXor" composition equation. - /// - public class AddXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddXor Instance { get; } = new AddXor(); /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.AddXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -33889,14 +81388,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -33904,7 +81421,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -33954,18 +81485,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -33973,7 +81523,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddXor(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.DarkenXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddXor(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.DarkenXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -34022,6 +81587,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -34041,11 +81607,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -34053,7 +81635,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -34078,31 +81662,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -34112,6 +81707,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -34136,10 +81732,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -34147,7 +81759,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -34172,43 +81786,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.DarkenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "SubtractXor" composition equation. + /// A pixel blender that implements the "LightenXor" composition equation. /// - public class SubtractXor : PixelBlender + public class LightenXor : PixelBlender { /// /// Gets the static instance of this blender. /// - public static SubtractXor Instance { get; } = new SubtractXor(); + public static LightenXor Instance { get; } = new LightenXor(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -34228,7 +81853,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -34388,7 +82013,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -34424,14 +82049,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -34482,7 +82107,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -34517,37 +82142,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "ScreenXor" composition equation. - /// - public class ScreenXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenXor Instance { get; } = new ScreenXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -34559,14 +82167,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -34574,7 +82200,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.LightenXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.LightenXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -34624,18 +82264,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -34643,7 +82302,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.LightenXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.LightenXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -34692,6 +82366,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -34711,11 +82386,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -34723,7 +82414,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -34748,31 +82441,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -34782,6 +82486,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -34806,10 +82511,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -34817,7 +82538,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -34842,43 +82565,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.LightenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "DarkenXor" composition equation. + /// A pixel blender that implements the "OverlayXor" composition equation. /// - public class DarkenXor : PixelBlender + public class OverlayXor : PixelBlender { /// /// Gets the static instance of this blender. /// - public static DarkenXor Instance { get; } = new DarkenXor(); + public static OverlayXor Instance { get; } = new OverlayXor(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -34898,7 +82632,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -35058,7 +82792,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -35094,14 +82828,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -35152,7 +82886,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -35187,37 +82921,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "LightenXor" composition equation. - /// - public class LightenXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenXor Instance { get; } = new LightenXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.LightenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -35229,14 +82946,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -35244,7 +82979,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -35294,18 +83043,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -35313,7 +83081,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenXor(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.OverlayXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.OverlayXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -35362,6 +83145,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -35381,11 +83165,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -35393,7 +83193,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -35418,31 +83220,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -35452,6 +83265,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -35476,10 +83290,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -35487,7 +83317,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -35512,43 +83344,54 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.OverlayXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "OverlayXor" composition equation. + /// A pixel blender that implements the "HardLightXor" composition equation. /// - public class OverlayXor : PixelBlender + public class HardLightXor : PixelBlender { /// /// Gets the static instance of this blender. /// - public static OverlayXor Instance { get; } = new OverlayXor(); + public static HardLightXor Instance { get; } = new HardLightXor(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -35568,7 +83411,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -35728,7 +83571,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); @@ -35764,14 +83607,14 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); @@ -35822,7 +83665,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); @@ -35857,37 +83700,20 @@ protected override void BlendFunction(Span destination, ReadOnlySpan - /// A pixel blender that implements the "HardLightXor" composition equation. - /// - public class HardLightXor : PixelBlender - { - /// - /// 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 TPixel.FromScaledVector4(PorterDuffFunctions.HardLightXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); - } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -35899,14 +83725,32 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -35914,7 +83758,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); + Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) { amount = Numerics.Clamp(amount, 0, 1); @@ -35964,18 +83822,37 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -35983,7 +83860,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source, amount); + Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -36032,6 +83924,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 vOne = Vector512.Create(1F); @@ -36051,11 +83944,27 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -36063,7 +83972,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 vOne = Vector256.Create(1F); @@ -36088,31 +83999,42 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) { if (Avx512F.IsSupported && destination.Length >= 4) { @@ -36122,6 +84044,7 @@ protected override void BlendFunction(Span destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector512 sourceBase = Vector512.Create( source.X, source.Y, source.Z, source.W, @@ -36146,10 +84069,26 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); } int remainder = Numerics.Modulo4(destination.Length); @@ -36157,7 +84096,8 @@ protected override void BlendFunction(Span destination, ReadOnlySpan destination, ReadOnlySpan backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); @@ -36182,24 +84123,35 @@ protected override void BlendFunction(Span destination, ReadOnlySpan.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + Vector4 blended = PorterDuffFunctions.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index c2439c24cc..e20e47f2bb 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -401,6 +401,450 @@ var blenders = new []{ } } } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 opacity = Vector512.Create(amount); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + + /// + protected override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + if (Avx512F.IsSupported && destination.Length >= 4) + { + // Divide by 4 as 4 elements per Vector4 and 16 per Vector512 + ref Vector512 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector512 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); + + ref Vector512 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector512 sourceBase = Vector512.Create( + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W, + source.X, source.Y, source.Z, source.W); + Vector512 vOne = Vector512.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + float amount0 = amountBase; + float amount1 = Unsafe.Add(ref amountBase, 1); + float amount2 = Unsafe.Add(ref amountBase, 2); + float amount3 = Unsafe.Add(ref amountBase, 3); + + // We need to create a Vector512 containing the current four amount values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 opacity = Vector512.Create( + amount0, amount0, amount0, amount0, + amount1, amount1, amount1, amount1, + amount2, amount2, amount2, amount2, + amount3, amount3, amount3, amount3); + opacity = Vector512.Min(Vector512.Max(Vector512.Zero, opacity), vOne); + + float coverage0 = coverageBase; + float coverage1 = Unsafe.Add(ref coverageBase, 1); + float coverage2 = Unsafe.Add(ref coverageBase, 2); + float coverage3 = Unsafe.Add(ref coverageBase, 3); + + // We need to create a Vector512 containing the current four coverage values + // taking up each quarter of the Vector512 and then clamp them. + Vector512 coverageVector = Vector512.Create( + coverage0, coverage0, coverage0, coverage0, + coverage1, coverage1, coverage1, coverage1, + coverage2, coverage2, coverage2, coverage2, + coverage3, coverage3, coverage3, coverage3); + coverageVector = Vector512.Min(Vector512.Max(Vector512.Zero, coverageVector), vOne); + + Vector512 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 4); + coverageBase = ref Unsafe.Add(ref coverageBase, 4); + } + + int remainder = Numerics.Modulo4(destination.Length); + if (remainder != 0) + { + for (int i = destination.Length - remainder; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } + else if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + ref float coverageBase = ref MemoryMarshal.GetReference(coverage); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + // We need to create a Vector256 containing the current and next coverage values + // taking up each half of the Vector256 and then clamp them. + Vector256 coverageVector = Vector256.Create( + Vector128.Create(coverageBase), + Vector128.Create(Unsafe.Add(ref coverageBase, 1))); + coverageVector = Avx.Min(Avx.Max(Vector256.Zero, coverageVector), vOne); + + Vector256 blended = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + coverageBase = ref Unsafe.Add(ref coverageBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + Vector4 blended = PorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); + } + } + } } <# diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index 948076fa32..b764432e8a 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -324,6 +324,65 @@ public static Vector512 HardLight(Vector512 backdrop, Vector512.Zero, color)); } + /// + /// Applies raster coverage to a Porter-Duff composition result. + /// + /// The backdrop vector. + /// The Porter-Duff composition result. + /// The coverage. Range 0..1. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 BlendWithCoverage(Vector4 backdrop, Vector4 source, float coverage) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + 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); + + Numerics.UnPremultiply(ref result); + return result; + } + + /// + /// Applies raster coverage to a Porter-Duff composition result. + /// + /// The backdrop vector. + /// The Porter-Duff composition result. + /// The coverage. Range 0..1. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 BlendWithCoverage(Vector256 backdrop, Vector256 source, Vector256 coverage) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 backdropPremultiplied = Avx.Blend(backdrop * backdropAlpha, backdropAlpha, BlendAlphaControl); + Vector256 sourcePremultiplied = Avx.Blend(source * sourceAlpha, sourceAlpha, BlendAlphaControl); + Vector256 result = Vector256_.MultiplyAdd(backdropPremultiplied, sourcePremultiplied - backdropPremultiplied, coverage); + + return Numerics.UnPremultiply(result, Avx.Permute(result, ShuffleAlphaControl)); + } + + /// + /// Applies raster coverage to a Porter-Duff composition result. + /// + /// The backdrop vector. + /// The Porter-Duff composition result. + /// The coverage. Range 0..1. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 BlendWithCoverage(Vector512 backdrop, Vector512 source, Vector512 coverage) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 alphaMask = AlphaMask512(); + Vector512 backdropPremultiplied = Vector512.ConditionalSelect(alphaMask, backdropAlpha, backdrop * backdropAlpha); + Vector512 sourcePremultiplied = Vector512.ConditionalSelect(alphaMask, sourceAlpha, source * sourceAlpha); + Vector512 result = Vector512_.MultiplyAdd(backdropPremultiplied, sourcePremultiplied - backdropPremultiplied, coverage); + + return Numerics.UnPremultiply(result, Vector512_.ShuffleNative(result, ShuffleAlphaControl)); + } + /// /// Helper function for Overlay and HardLight modes /// diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index fcd28796fb..0b1a51d74f 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.Numerics; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; namespace SixLabors.ImageSharp.PixelFormats; @@ -167,6 +168,162 @@ public void Blend( PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); } + /// + /// Blends 2 rows together with per-pixel coverage. + /// + /// the pixel format of the source span + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount, + ReadOnlySpan coverage) + where TPixelSrc : unmanaged, IPixel + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, maxLength, nameof(source.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + + using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 3); + this.BlendWithCoverage( + configuration, + destination, + background, + source, + amount, + coverage, + buffer.Memory.Span[..(maxLength * 3)]); + } + + /// + /// Blends 2 rows together with per-pixel coverage using caller-provided temporary vector scratch. + /// + /// the pixel format of the source span + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + /// Reusable temporary vector scratch with capacity for at least 3 rows. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount, + ReadOnlySpan coverage, + Span workingBuffer) + where TPixelSrc : unmanaged, IPixel + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, maxLength, nameof(source.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 3, nameof(workingBuffer.Length)); + + Span destinationVectors = workingBuffer[..maxLength]; + 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.BlendWithCoverageFunction(destinationVectors, backgroundVectors, sourceVectors, amount, coverage); + + PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); + } + + /// + /// Blends a row against a constant source color with per-pixel coverage. + /// + /// to use internally + /// the destination span + /// the background span + /// the source color + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + float amount, + ReadOnlySpan coverage) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + + using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 2); + this.BlendWithCoverage( + configuration, + destination, + background, + source, + amount, + coverage, + buffer.Memory.Span[..(maxLength * 2)]); + } + + /// + /// Blends a row against a constant source color with per-pixel coverage using caller-provided temporary vector scratch. + /// + /// to use internally + /// the destination span + /// the background span + /// the source color + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + /// Reusable temporary vector scratch with capacity for at least 2 rows. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + float amount, + ReadOnlySpan coverage, + Span workingBuffer) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 2, nameof(workingBuffer.Length)); + + Span destinationVectors = workingBuffer[..maxLength]; + Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); + + PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); + + this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount, coverage); + + PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); + } + /// /// Blends 2 rows together /// @@ -349,6 +506,206 @@ public void Blend( PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); } + /// + /// Blends 2 rows together with per-pixel coverage. + /// + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + => this.BlendWithCoverage(configuration, destination, background, source, amount, coverage); + + /// + /// Blends 2 rows together with per-pixel coverage using caller-provided temporary vector scratch. + /// + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + /// Reusable temporary vector scratch with capacity for at least 3 rows. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + ReadOnlySpan coverage, + Span workingBuffer) + => this.BlendWithCoverage(configuration, destination, background, source, amount, coverage, workingBuffer); + + /// + /// Blends 2 rows together with per-pixel coverage. + /// + /// the pixel format of the source span + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + where TPixelSrc : unmanaged, IPixel + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, maxLength, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + + using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 3); + this.BlendWithCoverage( + configuration, + destination, + background, + source, + amount, + coverage, + buffer.Memory.Span[..(maxLength * 3)]); + } + + /// + /// Blends a row against a constant source color with per-pixel coverage. + /// + /// to use internally + /// the destination span + /// the background span + /// the source color + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + + using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 2); + this.BlendWithCoverage( + configuration, + destination, + background, + source, + amount, + coverage, + buffer.Memory.Span[..(maxLength * 2)]); + } + + /// + /// Blends 2 rows together with per-pixel coverage using caller-provided temporary vector scratch. + /// + /// the pixel format of the source span + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + /// Reusable temporary vector scratch with capacity for at least 3 rows. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + ReadOnlySpan coverage, + Span workingBuffer) + where TPixelSrc : unmanaged, IPixel + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, maxLength, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 3, nameof(workingBuffer.Length)); + + Span destinationVectors = workingBuffer[..maxLength]; + 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.BlendWithCoverageFunction(destinationVectors, backgroundVectors, sourceVectors, amount, coverage); + + PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); + } + + /// + /// Blends a row against a constant source color with per-pixel coverage using caller-provided temporary vector scratch. + /// + /// to use internally + /// the destination span + /// the background span + /// the source color + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + /// Reusable temporary vector scratch with capacity for at least 2 rows. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + ReadOnlySpan amount, + ReadOnlySpan coverage, + Span workingBuffer) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 2, nameof(workingBuffer.Length)); + + Span destinationVectors = workingBuffer[..maxLength]; + Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); + + PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); + + this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount, coverage); + + PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); + } + /// /// Blend 2 rows together. /// @@ -412,4 +769,108 @@ protected abstract void BlendFunction( ReadOnlySpan background, Vector4 source, ReadOnlySpan amount); + + /// + /// Blend 2 rows together with per-pixel coverage. + /// + /// destination span + /// the background span + /// the source span + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + protected virtual void BlendWithCoverageFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount, + ReadOnlySpan coverage) + { + this.BlendFunction(destination, background, source, amount); + + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], destination[i], Numerics.Clamp(coverage[i], 0, 1F)); + } + } + + /// + /// Blend a row against a constant source color with per-pixel coverage. + /// + /// destination span + /// the background span + /// the source color vector + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + protected virtual void BlendWithCoverageFunction( + Span destination, + ReadOnlySpan background, + Vector4 source, + float amount, + ReadOnlySpan coverage) + { + this.BlendFunction(destination, background, source, amount); + + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], destination[i], Numerics.Clamp(coverage[i], 0, 1F)); + } + } + + /// + /// Blend 2 rows together with per-pixel coverage. + /// + /// destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + protected virtual void BlendWithCoverageFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + { + this.BlendFunction(destination, background, source, amount); + + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], destination[i], Numerics.Clamp(coverage[i], 0, 1F)); + } + } + + /// + /// Blend a row against a constant source color with per-pixel coverage. + /// + /// destination span + /// the background span + /// the source color vector + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + protected virtual void BlendWithCoverageFunction( + Span destination, + ReadOnlySpan background, + Vector4 source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + { + this.BlendFunction(destination, background, source, amount); + + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], destination[i], Numerics.Clamp(coverage[i], 0, 1F)); + } + } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs index a5e4e3048b..bcfad0117c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs @@ -38,6 +38,7 @@ public override void ToAbgr32(Configuration configuration, ReadOnlySpan source.CopyTo(destination.Slice(0, source.Length)); } + /// public override void FromVector4Destructive( Configuration configuration, @@ -45,7 +46,20 @@ public override void FromVector4Destructive( Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(PixelConversionModifiers.Scale)); + Guard.DestinationShouldNotBeTooShort(sourceVectors, destination, nameof(destination)); + + destination = destination[..sourceVectors.Length]; + Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers.Remove(PixelConversionModifiers.Scale)); + + Span destinationBytes = MemoryMarshal.Cast(destination); + + // The SIMD saturating conversion produces RGBA byte order. Reusing the destination + // buffer and shuffling it in place avoids the row-sized Rgba32 temporary used by + // the generic Rgba-compatible path for non-Rgba32 formats. + SimdUtils.NormalizedFloatToByteSaturate( + MemoryMarshal.Cast(sourceVectors), + destinationBytes); + PixelConverter.FromRgba32.ToAbgr32(destinationBytes, destinationBytes); } /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs index 30fed8d1c1..3ef93de9fc 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs @@ -38,6 +38,7 @@ public override void ToArgb32(Configuration configuration, ReadOnlySpan source.CopyTo(destination.Slice(0, source.Length)); } + /// public override void FromVector4Destructive( Configuration configuration, @@ -45,7 +46,20 @@ public override void FromVector4Destructive( Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(PixelConversionModifiers.Scale)); + Guard.DestinationShouldNotBeTooShort(sourceVectors, destination, nameof(destination)); + + destination = destination[..sourceVectors.Length]; + Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers.Remove(PixelConversionModifiers.Scale)); + + Span destinationBytes = MemoryMarshal.Cast(destination); + + // The SIMD saturating conversion produces RGBA byte order. Reusing the destination + // buffer and shuffling it in place avoids the row-sized Rgba32 temporary used by + // the generic Rgba-compatible path for non-Rgba32 formats. + SimdUtils.NormalizedFloatToByteSaturate( + MemoryMarshal.Cast(sourceVectors), + destinationBytes); + PixelConverter.FromRgba32.ToArgb32(destinationBytes, destinationBytes); } /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs index ae6be44012..6afcb64326 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs @@ -38,6 +38,7 @@ public override void ToBgra32(Configuration configuration, ReadOnlySpan source.CopyTo(destination.Slice(0, source.Length)); } + /// public override void FromVector4Destructive( Configuration configuration, @@ -45,7 +46,20 @@ public override void FromVector4Destructive( Span destination, PixelConversionModifiers modifiers) { - Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(PixelConversionModifiers.Scale)); + Guard.DestinationShouldNotBeTooShort(sourceVectors, destination, nameof(destination)); + + destination = destination[..sourceVectors.Length]; + Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers.Remove(PixelConversionModifiers.Scale)); + + Span destinationBytes = MemoryMarshal.Cast(destination); + + // The SIMD saturating conversion produces RGBA byte order. Reusing the destination + // buffer and shuffling it in place avoids the row-sized Rgba32 temporary used by + // the generic Rgba-compatible path for non-Rgba32 formats. + SimdUtils.NormalizedFloatToByteSaturate( + MemoryMarshal.Cast(sourceVectors), + destinationBytes); + PixelConverter.FromRgba32.ToBgra32(destinationBytes, destinationBytes); } /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude index ae29223deb..d53ed520b6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude @@ -152,6 +152,12 @@ using SixLabors.ImageSharp.PixelFormats.Utils; { removeTheseModifiers += " | PixelConversionModifiers.Premultiply"; } + + if (hasAlpha) + { + GenerateRgba32Compatible32BitVector4ConversionMethods(pixelType, removeTheseModifiers); + return; + } #> /// @@ -176,6 +182,45 @@ using SixLabors.ImageSharp.PixelFormats.Utils; <#+ } + void GenerateRgba32Compatible32BitVector4ConversionMethods(string pixelType, string removeTheseModifiers) + { +#> + + /// + public override void FromVector4Destructive( + Configuration configuration, + Span sourceVectors, + Span<<#=pixelType#>> destination, + PixelConversionModifiers modifiers) + { + Guard.DestinationShouldNotBeTooShort(sourceVectors, destination, nameof(destination)); + + destination = destination[..sourceVectors.Length]; + Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers.Remove(<#=removeTheseModifiers#>)); + + Span destinationBytes = MemoryMarshal.Cast<<#=pixelType#>, byte>(destination); + + // The SIMD saturating conversion produces RGBA byte order. Reusing the destination + // buffer and shuffling it in place avoids the row-sized Rgba32 temporary used by + // the generic Rgba-compatible path for non-Rgba32 formats. + SimdUtils.NormalizedFloatToByteSaturate( + MemoryMarshal.Cast(sourceVectors), + destinationBytes); + PixelConverter.FromRgba32.To<#=pixelType#>(destinationBytes, destinationBytes); + } + + /// + public override void ToVector4( + Configuration configuration, + ReadOnlySpan<<#=pixelType#>> source, + Span destination, + PixelConversionModifiers modifiers) + { + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, source, destination, modifiers.Remove(<#=removeTheseModifiers#>)); + } +<#+ + } + void GenerateAllDefaultConversionMethods(string pixelType) { GenerateDefaultSelfConversionMethods(pixelType); diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs index ddd79bb756..36ddad6c64 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs @@ -328,6 +328,214 @@ public void Blend_WithSourceSpanAmountSpanAndWorkingBuffer() Assert.Equal(source[1], destination[1]); } + [Fact] + public void BlendWithCoverage_WithConstantSourceAndSingleAmount() + { + PixelBlender blender = new DefaultPixelBlenders.NormalSrc(); + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(255, 0, 0), + new(255, 0, 0) + ]; + + Rgba32 source = new(0, 0, 255); + float[] coverage = [0F, .5F, 1F]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, 1F, coverage); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 0, 128), destination[1]); + Assert.Equal(source, destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithConstantSourceSingleAmountAndWorkingBuffer() + { + PixelBlender blender = new DefaultPixelBlenders.NormalSrc(); + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(255, 0, 0), + new(255, 0, 0) + ]; + + Rgba32 source = new(0, 0, 255); + float[] coverage = [0F, .5F, 1F]; + Vector4[] workingBuffer = new Vector4[destination.Length * 2]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, 1F, coverage, workingBuffer); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 0, 128), destination[1]); + Assert.Equal(source, destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithSourceSpanAndSingleAmount() + { + PixelBlender blender = new DefaultPixelBlenders.NormalSrc(); + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(0, 255, 0), + new(0, 0, 255) + ]; + + Rgba32[] source = + [ + new(0, 0, 255), + new(255, 0, 0), + new(0, 255, 0) + ]; + + float[] coverage = [0F, .5F, 1F]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, 1F, coverage); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 128, 0), destination[1]); + Assert.Equal(source[2], destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithSourceSpanSingleAmountAndWorkingBuffer() + { + PixelBlender blender = new DefaultPixelBlenders.NormalSrc(); + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(0, 255, 0), + new(0, 0, 255) + ]; + + Rgba32[] source = + [ + new(0, 0, 255), + new(255, 0, 0), + new(0, 255, 0) + ]; + + float[] coverage = [0F, .5F, 1F]; + Vector4[] workingBuffer = new Vector4[destination.Length * 3]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, 1F, coverage, workingBuffer); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 128, 0), destination[1]); + Assert.Equal(source[2], destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithConstantSourceAndAmountSpan() + { + PixelBlender blender = new DefaultPixelBlenders.NormalSrc(); + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(255, 0, 0), + new(255, 0, 0) + ]; + + Rgba32 source = new(0, 0, 255); + float[] amount = [1F, 1F, 1F]; + float[] coverage = [0F, .5F, 1F]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, amount, coverage); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 0, 128), destination[1]); + Assert.Equal(source, destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithConstantSourceAmountSpanAndWorkingBuffer() + { + PixelBlender blender = new DefaultPixelBlenders.NormalSrc(); + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(255, 0, 0), + new(255, 0, 0) + ]; + + Rgba32 source = new(0, 0, 255); + float[] amount = [1F, 1F, 1F]; + float[] coverage = [0F, .5F, 1F]; + Vector4[] workingBuffer = new Vector4[destination.Length * 2]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, amount, coverage, workingBuffer); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 0, 128), destination[1]); + Assert.Equal(source, destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithSourceSpanAndAmountSpan() + { + PixelBlender blender = new DefaultPixelBlenders.NormalSrc(); + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(0, 255, 0), + new(0, 0, 255) + ]; + + Rgba32[] source = + [ + new(0, 0, 255), + new(255, 0, 0), + new(0, 255, 0) + ]; + + float[] amount = [1F, 1F, 1F]; + float[] coverage = [0F, .5F, 1F]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, amount, coverage); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 128, 0), destination[1]); + Assert.Equal(source[2], destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithSourceSpanAmountSpanAndWorkingBuffer() + { + PixelBlender blender = new DefaultPixelBlenders.NormalSrc(); + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(0, 255, 0), + new(0, 0, 255) + ]; + + Rgba32[] source = + [ + new(0, 0, 255), + new(255, 0, 0), + new(0, 255, 0) + ]; + + float[] amount = [1F, 1F, 1F]; + float[] coverage = [0F, .5F, 1F]; + Vector4[] workingBuffer = new Vector4[destination.Length * 3]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, amount, coverage, workingBuffer); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 128, 0), destination[1]); + Assert.Equal(source[2], destination[2]); + } + public static TheoryData ColorBlendingExpectedResults = new() { { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelColorBlendingMode.Normal, Color.MidnightBlue.ToPixel() }, @@ -421,6 +629,7 @@ private static void ExerciseBlender(PixelBlender blender) Rgba32 background = Color.MistyRose.ToPixel(); Rgba32 source = Color.MidnightBlue.ToPixel(); float[] amount = [1F, 1F, 1F, 1F]; + float[] coverage = [1F, 1F, 1F, 1F]; Rgba32 expected = blender.Blend(background, source, 1F); @@ -441,5 +650,17 @@ private static void ExerciseBlender(PixelBlender blender) 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); + Assert.All(destination, x => Assert.Equal(expected, x)); + + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, source, 1F, coverage, constantSourceBuffer); + Assert.All(destination, x => Assert.Equal(expected, x)); + + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, sourceSpan, amount, coverage, sourceSpanBuffer); + Assert.All(destination, x => Assert.Equal(expected, x)); + + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, source, amount, coverage, constantSourceBuffer); + Assert.All(destination, x => Assert.Equal(expected, x)); } }