diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 46f659c451..ace6a4306e 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -27,7 +27,7 @@ jobs: git config --global core.longpaths true - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 submodules: recursive @@ -137,7 +137,7 @@ jobs: git config --global core.longpaths true - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 submodules: recursive @@ -209,7 +209,7 @@ jobs: XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit - name: Export Failed Output - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 if: failure() with: name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip @@ -227,7 +227,7 @@ jobs: git config --global core.longpaths true - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 submodules: recursive diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index a095d1abeb..16ae0317dc 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -31,7 +31,7 @@ jobs: git config --global core.longpaths true - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 submodules: recursive @@ -86,7 +86,7 @@ jobs: XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit - name: Export Failed Output - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 if: failure() with: name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip diff --git a/.gitignore b/.gitignore index fadf36964c..a8d2917be7 100644 --- a/.gitignore +++ b/.gitignore @@ -227,3 +227,5 @@ artifacts/ #lfs hooks/** lfs/** + +.dotnet diff --git a/README.md b/README.md index dc30734792..4d4375dbf5 100644 --- a/README.md +++ b/README.md @@ -10,16 +10,14 @@ SixLabors.ImageSharp [![Build Status](https://img.shields.io/github/actions/workflow/status/SixLabors/ImageSharp/build-and-test.yml?branch=main)](https://github.com/SixLabors/ImageSharp/actions) [![codecov](https://codecov.io/gh/SixLabors/ImageSharp/graph/badge.svg?token=g2WJwz770q)](https://codecov.io/gh/SixLabors/ImageSharp) [![License: Six Labors Split](https://img.shields.io/badge/license-Six%20Labors%20Split-%23e30183)](https://github.com/SixLabors/ImageSharp/blob/main/LICENSE) -[![Twitter](https://img.shields.io/twitter/url/http/shields.io.svg?style=flat&logo=twitter)](https://twitter.com/intent/tweet?hashtags=imagesharp,dotnet,oss&text=ImageSharp.+A+new+cross-platform+2D+graphics+API+in+C%23&url=https%3a%2f%2fgithub.com%2fSixLabors%2fImageSharp&via=sixlabors) -### **ImageSharp** is a new, fully featured, fully managed, cross-platform, 2D graphics API. +### **ImageSharp** is a high-performance, fully managed, cross-platform 2D graphics API. -ImageSharp is a new, fully featured, fully managed, cross-platform, 2D graphics library. -Designed to simplify image processing, ImageSharp brings you an incredibly powerful yet beautifully simple API. +ImageSharp is a mature, fully featured, high-performance image processing and graphics library for .NET, built for workloads across device, cloud, and embedded/IoT scenarios. -ImageSharp is designed from the ground up to be flexible and extensible. The library provides API endpoints for common image processing operations and the building blocks to allow for the development of additional operations. +Designed from the ground up to balance performance, portability, and ease of use, ImageSharp provides a powerful yet approachable API for common image processing tasks, along with the low-level building blocks needed to extend the library for specialized workflows. Built against [.NET 8](https://docs.microsoft.com/en-us/dotnet/standard/net-standard), ImageSharp can be used in device, cloud, and embedded/IoT scenarios. diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index a1de790c3d..230526fd4a 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -1548,6 +1548,12 @@ internal sealed class BmpDecoderCore : ImageDecoderCore case BmpFileMarkerType.Bitmap: if (this.fileHeader.HasValue) { + if (this.fileHeader.Value.Offset > stream.Length) + { + BmpThrowHelper.ThrowInvalidImageContentException( + $"Pixel data offset {this.fileHeader.Value.Offset} exceeds file size {stream.Length}."); + } + colorMapSizeBytes = this.fileHeader.Value.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize; } else diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 7825955e7a..d4517e9f19 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -519,6 +519,11 @@ internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData fileMarker = FindNextFileMarker(stream); } + if (!metadataOnly && this.Frame is null) + { + JpegThrowHelper.ThrowInvalidImageContentException("No readable SOFn (Start Of Frame) marker found."); + } + this.Metadata.GetJpegMetadata().Interleaved = this.Frame.Interleaved; } diff --git a/src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs b/src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs index 8af0ac8ca7..cce4d16b84 100644 --- a/src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs +++ b/src/ImageSharp/Formats/Png/Chunks/PngPhysical.cs @@ -46,6 +46,11 @@ internal readonly struct PngPhysical /// The parsed PhysicalChunkData. public static PngPhysical Parse(ReadOnlySpan data) { + if (data.Length < 9) + { + PngThrowHelper.ThrowInvalidImageContentException("pHYs chunk is too short"); + } + uint hResolution = BinaryPrimitives.ReadUInt32BigEndian(data[..4]); uint vResolution = BinaryPrimitives.ReadUInt32BigEndian(data.Slice(4, 4)); byte unit = data[8]; diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 271474a7e5..8962182679 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -1253,6 +1253,12 @@ internal sealed class PngDecoderCore : ImageDecoderCore ReadOnlySpan rgbTable = MemoryMarshal.Cast(palette); Color.FromPixel(rgbTable, colorTable); + // The tRNS chunk must not contain more alpha values than there are palette entries. + if (alpha.Length > colorTable.Length) + { + alpha = alpha.Slice(0, colorTable.Length); + } + if (alpha.Length > 0) { // The alpha chunk may contain as many transparency entries as there are palette entries @@ -1402,26 +1408,31 @@ internal sealed class PngDecoderCore : ImageDecoderCore return; } - int zeroIndex = data.IndexOf((byte)0); - if (zeroIndex is < PngConstants.MinTextKeywordLength or > PngConstants.MaxTextKeywordLength) + int keywordEnd = data.IndexOf((byte)0); + if (keywordEnd is < PngConstants.MinTextKeywordLength or > PngConstants.MaxTextKeywordLength) { return; } - byte compressionMethod = data[zeroIndex + 1]; + if (keywordEnd < 0 || keywordEnd + 2 > data.Length) + { + return; // Not enough data for keyword + null + compression method. + } + + byte compressionMethod = data[keywordEnd + 1]; if (compressionMethod != 0) { // Only compression method 0 is supported (zlib datastream with deflate compression). return; } - ReadOnlySpan keywordBytes = data[..zeroIndex]; + ReadOnlySpan keywordBytes = data[..keywordEnd]; if (!TryReadTextKeyword(keywordBytes, out string name)) { return; } - ReadOnlySpan compressedData = data[(zeroIndex + 2)..]; + ReadOnlySpan compressedData = data[(keywordEnd + 2)..]; if (this.TryDecompressTextData(compressedData, PngConstants.Encoding, out string? uncompressed) && !TryReadTextChunkMetadata(baseMetadata, name, uncompressed)) @@ -1932,6 +1943,11 @@ internal sealed class PngDecoderCore : ImageDecoderCore return; } + if (zeroIndexKeyword < 0 || zeroIndexKeyword + 4 > data.Length) + { + return; // Not enough data for keyword + null + flag + method + language. + } + byte compressionFlag = data[zeroIndexKeyword + 1]; if (compressionFlag is not (0 or 1)) { @@ -1956,6 +1972,11 @@ internal sealed class PngDecoderCore : ImageDecoderCore int translatedKeywordStartIdx = langStartIdx + languageLength + 1; int translatedKeywordLength = data[translatedKeywordStartIdx..].IndexOf((byte)0); + if (translatedKeywordLength < 0) + { + return; + } + string translatedKeyword = PngConstants.TranslatedEncoding.GetString(data.Slice(translatedKeywordStartIdx, translatedKeywordLength)); ReadOnlySpan keywordBytes = data[..zeroIndexKeyword]; diff --git a/src/ImageSharp/Formats/Png/PngThrowHelper.cs b/src/ImageSharp/Formats/Png/PngThrowHelper.cs index 8dc70e1d9a..80c51ef6e5 100644 --- a/src/ImageSharp/Formats/Png/PngThrowHelper.cs +++ b/src/ImageSharp/Formats/Png/PngThrowHelper.cs @@ -9,8 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Png; internal static class PngThrowHelper { [DoesNotReturn] - public static void ThrowInvalidImageContentException(string errorMessage, Exception innerException) - => throw new InvalidImageContentException(errorMessage, innerException); + public static void ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage); [DoesNotReturn] public static void ThrowInvalidHeader() => throw new InvalidImageContentException("PNG Image must contain a header chunk and it must be located before any other chunks."); diff --git a/src/ImageSharp/GraphicsOptions.cs b/src/ImageSharp/GraphicsOptions.cs index dc3d179027..e056596512 100644 --- a/src/ImageSharp/GraphicsOptions.cs +++ b/src/ImageSharp/GraphicsOptions.cs @@ -6,11 +6,12 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp; /// -/// Options for influencing the drawing functions. +/// Provides configuration for controlling how graphics operations are rendered, +/// including antialiasing, pixel blending, alpha composition, and coverage thresholding. /// public class GraphicsOptions : IDeepCloneable { - private int antialiasSubpixelDepth = 16; + private float antialiasThreshold = .5F; private float blendPercentage = 1F; /// @@ -24,61 +25,62 @@ public class GraphicsOptions : IDeepCloneable { this.AlphaCompositionMode = source.AlphaCompositionMode; this.Antialias = source.Antialias; - this.AntialiasSubpixelDepth = source.AntialiasSubpixelDepth; + this.AntialiasThreshold = source.AntialiasThreshold; this.BlendPercentage = source.BlendPercentage; this.ColorBlendingMode = source.ColorBlendingMode; } /// /// Gets or sets a value indicating whether antialiasing should be applied. - /// Defaults to true. + /// When , edges are rendered with smooth sub-pixel coverage. + /// When , coverage is snapped to binary (fully opaque or fully transparent) + /// using as the cutoff. + /// Defaults to . /// public bool Antialias { get; set; } = true; /// - /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. - /// Defaults to 16. + /// Gets or sets the coverage threshold used when is . + /// Pixels with antialiased coverage above this value are rendered as fully opaque; + /// pixels below are discarded. Valid range is 0 to 1. Lower values preserve more + /// thin features at small sizes. Defaults to 0.5F. /// - public int AntialiasSubpixelDepth + public float AntialiasThreshold { - get - { - return this.antialiasSubpixelDepth; - } + get => this.antialiasThreshold; set { - Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.AntialiasSubpixelDepth)); - this.antialiasSubpixelDepth = value; + Guard.MustBeBetweenOrEqualTo(value, 0F, 1F, nameof(this.AntialiasThreshold)); + this.antialiasThreshold = value; } } /// - /// Gets or sets a value between indicating the blending percentage to apply to the drawing operation. - /// Range 0..1; Defaults to 1. + /// Gets or sets the blending percentage applied to the drawing operation. + /// A value of 1.0 applies the operation at full strength; 0.0 makes it invisible. + /// Valid range is 0 to 1. Defaults to 1.0F. /// public float BlendPercentage { - get - { - return this.blendPercentage; - } + get => this.blendPercentage; set { - Guard.MustBeBetweenOrEqualTo(value, 0, 1F, nameof(this.BlendPercentage)); + Guard.MustBeBetweenOrEqualTo(value, 0F, 1F, nameof(this.BlendPercentage)); this.blendPercentage = value; } } /// - /// Gets or sets a value indicating the color blending mode to apply to the drawing operation. + /// Gets or sets the color blending mode used to combine source and destination pixel colors. /// Defaults to . /// public PixelColorBlendingMode ColorBlendingMode { get; set; } = PixelColorBlendingMode.Normal; /// - /// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation + /// Gets or sets the alpha composition mode that determines how source and destination alpha + /// channels are combined using Porter-Duff operators. /// Defaults to . /// public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } = PixelAlphaCompositionMode.SrcOver; diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index f62d3c6761..7cd9cc57ad 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -81,8 +81,10 @@ internal static class DefaultPixelBlenders } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -90,65 +92,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.NormalSrc(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.NormalSrc(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -157,34 +129,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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); + destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -193,9 +175,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -207,10 +189,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -218,33 +199,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.NormalSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.NormalSrc(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -264,7 +245,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -274,21 +255,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -296,65 +279,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -363,34 +316,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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); + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -399,9 +362,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -413,10 +376,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrc(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -424,33 +386,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -470,7 +432,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -480,21 +442,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -502,65 +466,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.AddSrc(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.AddSrc(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -569,34 +503,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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); + destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -605,9 +549,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -619,10 +563,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrc(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -630,33 +573,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.AddSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.AddSrc(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -676,7 +619,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -686,21 +629,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -708,65 +653,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -775,34 +690,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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); + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -811,9 +736,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -825,10 +750,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrc(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -836,33 +760,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -882,7 +806,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -892,21 +816,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -914,65 +840,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -981,34 +877,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -1017,9 +923,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -1031,10 +937,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcAtop(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -1042,33 +947,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -1088,7 +993,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + 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); @@ -1098,21 +1003,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -1120,65 +1027,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + 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.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, amount); } } } - } - - /// - /// 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) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -1187,34 +1064,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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.AddSrcAtop(background[i], source[i], amount); + 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.AddSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -1223,9 +1110,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -1237,10 +1124,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + 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); } @@ -1248,33 +1134,33 @@ internal static class DefaultPixelBlenders { // 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)); + 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.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "SubtractSrcAtop" composition equation. + /// A pixel blender that implements the "LightenSrc" composition equation. /// - public class SubtractSrcAtop : PixelBlender + public class LightenSrc : PixelBlender { /// /// Gets the static instance of this blender. /// - public static SubtractSrcAtop Instance { get; } = new SubtractSrcAtop(); + public static LightenSrc Instance { get; } = new LightenSrc(); /// 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.LightenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1294,7 +1180,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + 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); @@ -1304,21 +1190,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -1326,65 +1214,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + 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.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -1393,34 +1251,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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.ScreenSrcAtop(background[i], source[i], amount); + 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.ScreenSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -1429,9 +1297,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -1443,10 +1311,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + 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); } @@ -1454,33 +1321,33 @@ internal static class DefaultPixelBlenders { // 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)); + 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.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "DarkenSrcAtop" composition equation. + /// A pixel blender that implements the "OverlaySrc" composition equation. /// - public class DarkenSrcAtop : PixelBlender + public class OverlaySrc : PixelBlender { /// /// Gets the static instance of this blender. /// - public static DarkenSrcAtop Instance { get; } = new DarkenSrcAtop(); + public static OverlaySrc Instance { get; } = new OverlaySrc(); /// 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.OverlaySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1500,7 +1367,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + 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); @@ -1510,21 +1377,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -1532,65 +1401,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + 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.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -1599,34 +1438,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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.LightenSrcAtop(background[i], source[i], amount); + 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.LightenSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -1635,9 +1484,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -1649,10 +1498,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + 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); } @@ -1660,33 +1508,33 @@ internal static class DefaultPixelBlenders { // 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)); + 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.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "OverlaySrcAtop" composition equation. + /// A pixel blender that implements the "HardLightSrc" composition equation. /// - public class OverlaySrcAtop : PixelBlender + public class HardLightSrc : PixelBlender { /// /// Gets the static instance of this blender. /// - public static OverlaySrcAtop Instance { get; } = new OverlaySrcAtop(); + public static HardLightSrc Instance { get; } = new HardLightSrc(); /// 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.HardLightSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1706,7 +1554,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + 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); @@ -1716,21 +1564,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -1738,65 +1588,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + 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.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -1805,34 +1625,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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.HardLightSrcAtop(background[i], source[i], amount); + 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.HardLightSrcAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -1841,9 +1671,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -1855,10 +1685,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + 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); } @@ -1866,33 +1695,33 @@ internal static class DefaultPixelBlenders { // 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)); + 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.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "NormalSrcOver" composition equation. + /// A pixel blender that implements the "NormalSrcAtop" composition equation. /// - public class NormalSrcOver : PixelBlender + public class NormalSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. /// - public static NormalSrcOver Instance { get; } = new NormalSrcOver(); + public static NormalSrcAtop Instance { get; } = new NormalSrcAtop(); /// 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.NormalSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -1912,7 +1741,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + 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); @@ -1922,14 +1751,52 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); + 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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); } } } @@ -1958,7 +1825,7 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + 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); @@ -1969,33 +1836,79 @@ internal static class DefaultPixelBlenders { // 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)); + 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.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } /// - /// A pixel blender that implements the "MultiplySrcOver" composition equation. + /// A pixel blender that implements the "MultiplySrcAtop" composition equation. /// - public class MultiplySrcOver : PixelBlender + public class MultiplySrcAtop : PixelBlender { /// /// Gets the static instance of this blender. /// - public static MultiplySrcOver Instance { get; } = new MultiplySrcOver(); + public static MultiplySrcAtop Instance { get; } = new MultiplySrcAtop(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2015,7 +1928,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + 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); @@ -2025,21 +1938,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -2047,65 +1962,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + 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.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, amount); } } } - } - - /// - /// 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) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -2114,34 +1999,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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.AddSrcOver(background[i], source[i], amount); + 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.AddSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -2150,9 +2045,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -2164,10 +2059,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + 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); } @@ -2175,33 +2069,33 @@ internal static class DefaultPixelBlenders { // 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)); + 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.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } } } /// - /// A pixel blender that implements the "SubtractSrcOver" composition equation. + /// A pixel blender that implements the "AddSrcAtop" composition equation. /// - public class SubtractSrcOver : PixelBlender + public class AddSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. /// - public static SubtractSrcOver Instance { get; } = new SubtractSrcOver(); + public static AddSrcAtop Instance { get; } = new AddSrcAtop(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2221,7 +2115,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + 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); @@ -2231,14 +2125,52 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); + 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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); } } } @@ -2267,7 +2199,7 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + 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); @@ -2278,33 +2210,79 @@ internal static class DefaultPixelBlenders { // 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)); + 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.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); } } } } /// - /// A pixel blender that implements the "ScreenSrcOver" composition equation. + /// A pixel blender that implements the "SubtractSrcAtop" composition equation. /// - public class ScreenSrcOver : PixelBlender + public class SubtractSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. /// - public static ScreenSrcOver Instance { get; } = new ScreenSrcOver(); + public static SubtractSrcAtop Instance { get; } = new SubtractSrcAtop(); /// 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.SubtractSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); } /// @@ -2324,7 +2302,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + 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); @@ -2334,21 +2312,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -2356,65 +2336,9946 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + 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.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source, amount); } } } - } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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 (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 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)); + } + } + } + } + + /// + /// 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) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "LightenDestAtop" composition equation. + /// + public class LightenDestAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static LightenDestAtop Instance { get; } = new LightenDestAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + } + + /// + /// 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) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + } + + /// + /// A pixel blender that implements the "HardLightDestAtop" composition equation. + /// + public class HardLightDestAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static HardLightDestAtop Instance { get; } = new HardLightDestAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + } + + /// + /// 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) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + } /// - /// A pixel blender that implements the "DarkenSrcOver" composition equation. + /// A pixel blender that implements the "MultiplyDestOver" composition equation. /// - public class DarkenSrcOver : PixelBlender + public class MultiplyDestOver : PixelBlender { /// /// Gets the static instance of this blender. /// - public static DarkenSrcOver Instance { get; } = new DarkenSrcOver(); + public static MultiplyDestOver Instance { get; } = new MultiplyDestOver(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source, amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + } + + /// + /// 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) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); + } + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestOver(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestOver(background[i], source, amount); + } + } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -2423,34 +12284,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -2459,9 +12330,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -2473,10 +12344,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcOver(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -2484,33 +12354,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.AddDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.AddDestOver(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -2530,7 +12400,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -2540,21 +12410,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -2562,65 +12434,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -2629,34 +12471,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -2665,9 +12517,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -2679,10 +12531,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcOver(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -2690,33 +12541,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -2736,7 +12587,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -2746,21 +12597,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -2768,65 +12621,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -2835,34 +12658,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -2871,9 +12704,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -2885,10 +12718,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalSrcIn(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -2896,33 +12728,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -2942,7 +12774,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -2952,21 +12784,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -2974,65 +12808,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); - } - } - } - } - - /// - /// 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))); + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source, amount); + } + } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -3041,34 +12845,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -3077,9 +12891,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -3091,10 +12905,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddSrcIn(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -3102,33 +12915,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -3148,7 +12961,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -3158,21 +12971,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -3180,65 +12995,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -3247,34 +13032,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -3283,9 +13078,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -3297,10 +13092,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenSrcIn(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -3308,33 +13102,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -3354,7 +13148,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -3364,21 +13158,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -3386,65 +13182,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -3453,34 +13219,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -3489,9 +13265,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -3503,10 +13279,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenSrcIn(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -3514,33 +13289,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -3560,7 +13335,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -3570,21 +13345,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -3592,65 +13369,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source, amount); } } else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[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))); + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source, amount); + } + } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -3659,34 +13406,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -3695,9 +13452,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -3709,10 +13466,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightSrcIn(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -3720,33 +13476,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -3766,7 +13522,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -3776,21 +13532,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -3798,65 +13556,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -3865,34 +13593,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -3901,9 +13639,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -3915,10 +13653,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplySrcOut(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -3926,33 +13663,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -3972,7 +13709,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -3982,21 +13719,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -4004,65 +13743,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -4071,34 +13780,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -4107,9 +13826,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -4121,10 +13840,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractSrcOut(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -4132,33 +13850,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -4178,7 +13896,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -4188,21 +13906,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -4210,65 +13930,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); - } - } - } - } - - /// - /// 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))); + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source, amount); + } + } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -4277,34 +13967,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -4313,9 +14013,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -4327,10 +14027,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenSrcOut(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -4338,33 +14037,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -4384,7 +14083,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -4394,21 +14093,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -4416,65 +14117,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -4483,34 +14154,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -4519,9 +14200,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -4533,10 +14214,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlaySrcOut(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -4544,33 +14224,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -4590,7 +14270,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -4600,21 +14280,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -4622,65 +14304,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -4689,34 +14341,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -4725,9 +14387,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -4739,10 +14401,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDest(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -4750,33 +14411,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -4796,7 +14457,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -4806,21 +14467,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -4828,65 +14491,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source, amount); } } else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[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))); + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source, amount); + } + } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -4895,34 +14528,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -4931,9 +14574,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -4945,10 +14588,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDest(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -4956,33 +14598,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -5002,7 +14644,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -5012,21 +14654,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -5034,65 +14678,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -5101,34 +14715,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -5137,9 +14761,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -5151,10 +14775,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDest(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -5162,33 +14785,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -5208,7 +14831,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -5218,21 +14841,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -5240,65 +14865,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -5307,34 +14902,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -5343,9 +14948,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -5357,10 +14962,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDest(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -5368,33 +14972,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -5414,7 +15018,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -5424,21 +15028,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -5446,65 +15052,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); - } - } - } - } - - /// - /// 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))); + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source, amount); + } + } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -5513,34 +15089,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -5549,9 +15135,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -5563,10 +15149,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDest(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -5574,33 +15159,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -5620,7 +15205,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -5630,21 +15215,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -5652,65 +15239,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -5719,34 +15276,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -5755,9 +15322,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -5769,10 +15336,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestAtop(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -5780,33 +15346,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -5826,7 +15392,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -5836,21 +15402,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -5858,65 +15426,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -5925,34 +15463,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -5961,9 +15509,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -5975,10 +15523,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestAtop(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -5986,33 +15533,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -6032,7 +15579,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -6042,21 +15589,23 @@ internal static class DefaultPixelBlenders { // 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); + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -6064,65 +15613,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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)); + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source, amount); } } else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDestAtop" composition equation. - /// - public class DarkenDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestAtop Instance { get; } = new DarkenDestAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source, amount); + } + } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -6131,34 +15650,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -6167,9 +15696,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -6181,10 +15710,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestAtop(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -6192,33 +15720,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -6238,7 +15766,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -6248,21 +15776,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -6270,65 +15800,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -6337,34 +15837,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -6373,9 +15883,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -6387,10 +15897,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestAtop(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -6398,33 +15907,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -6444,7 +15953,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -6454,21 +15963,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -6476,65 +15987,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -6543,34 +16024,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -6579,9 +16070,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -6593,10 +16084,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestOver(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -6604,33 +16094,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -6650,7 +16140,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -6660,21 +16150,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -6682,65 +16174,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// 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))); + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source, amount); + } + } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -6749,34 +16211,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -6785,9 +16257,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -6799,10 +16271,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestOver(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -6810,33 +16281,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -6856,7 +16327,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -6866,21 +16337,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -6888,65 +16361,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -6955,34 +16398,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -6991,9 +16444,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -7005,10 +16458,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestOver(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -7016,33 +16468,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -7062,7 +16514,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -7072,21 +16524,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -7094,65 +16548,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -7161,34 +16585,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -7197,9 +16631,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -7211,10 +16645,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestOver(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -7222,33 +16655,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -7268,7 +16701,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -7278,21 +16711,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -7300,65 +16735,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source, amount); } } else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDestOver" composition equation. - /// - public class HardLightDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestOver Instance { get; } = new HardLightDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source, amount); + } + } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -7367,34 +16772,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -7403,9 +16818,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -7417,10 +16832,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestOver(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -7428,33 +16842,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -7474,7 +16888,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -7484,21 +16898,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -7506,65 +16922,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.NormalClear(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.NormalClear(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -7573,34 +16959,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -7609,9 +17005,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -7623,10 +17019,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyDestIn(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -7634,33 +17029,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.NormalClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.NormalClear(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -7680,7 +17075,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -7690,21 +17085,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -7712,65 +17109,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -7779,34 +17146,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -7815,9 +17192,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -7829,10 +17206,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractDestIn(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -7840,33 +17216,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -7886,7 +17262,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -7896,21 +17272,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -7918,65 +17296,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// 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))); + destination[i] = PorterDuffFunctions.AddClear(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddClear(background[i], source, amount); + } + } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -7985,34 +17333,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -8021,9 +17379,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -8035,10 +17393,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenDestIn(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -8046,33 +17403,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.AddClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.AddClear(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -8092,7 +17449,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -8102,21 +17459,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -8124,65 +17483,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -8191,34 +17520,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -8227,9 +17566,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -8241,10 +17580,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayDestIn(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -8252,33 +17590,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -8298,7 +17636,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -8308,21 +17646,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -8330,65 +17670,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -8397,34 +17707,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -8433,9 +17753,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -8447,10 +17767,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalDestOut(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -8458,33 +17777,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -8504,7 +17823,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -8514,21 +17833,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -8536,65 +17857,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source, amount); } } else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDestOut" composition equation. - /// - public class AddDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestOut Instance { get; } = new AddDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1))); + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source, amount); + } + } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -8603,34 +17894,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -8639,9 +17940,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -8653,10 +17954,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddDestOut(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -8664,33 +17964,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -8710,7 +18010,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -8720,21 +18020,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -8742,65 +18044,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenClear(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenClear(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -8809,34 +18081,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -8845,9 +18127,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -8859,10 +18141,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenDestOut(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -8870,33 +18151,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.LightenClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenClear(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -8916,7 +18197,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -8926,21 +18207,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -8948,65 +18231,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -9015,34 +18268,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -9051,9 +18314,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -9065,10 +18328,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenDestOut(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -9076,33 +18338,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -9122,7 +18384,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -9132,21 +18394,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -9154,65 +18418,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// 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))); + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source, amount); + } + } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -9221,34 +18455,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -9257,9 +18501,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -9271,10 +18515,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.HardLightDestOut(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -9282,33 +18525,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -9328,7 +18571,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -9338,21 +18581,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -9360,65 +18605,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.NormalXor(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.NormalXor(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -9427,34 +18642,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -9463,9 +18688,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -9477,10 +18702,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.MultiplyClear(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -9488,33 +18712,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.NormalXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.NormalXor(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -9534,7 +18758,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -9544,21 +18768,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -9566,65 +18792,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -9633,34 +18829,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -9669,9 +18875,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -9683,10 +18889,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.SubtractClear(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -9694,33 +18899,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -9740,7 +18945,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -9750,21 +18955,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -9772,65 +18979,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.AddXor(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.AddXor(background[i], source, amount); } } } - } - - /// - /// 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) - { - amount = Numerics.Clamp(amount, 0, 1); - + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -9839,34 +19016,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -9875,9 +19062,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -9889,10 +19076,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.DarkenClear(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -9900,33 +19086,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.AddXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.AddXor(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -9946,7 +19132,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -9956,21 +19142,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -9978,65 +19166,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -10045,34 +19203,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -10081,9 +19249,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -10095,10 +19263,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.OverlayClear(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -10106,33 +19273,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -10152,7 +19319,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -10162,21 +19329,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -10184,65 +19353,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -10251,34 +19390,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -10287,9 +19436,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -10301,10 +19450,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.NormalXor(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -10312,33 +19460,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -10358,7 +19506,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -10368,21 +19516,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -10390,65 +19540,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -10457,34 +19577,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddXor(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); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(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); + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -10493,9 +19623,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -10507,10 +19637,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.AddXor(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -10518,33 +19647,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -10564,7 +19693,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -10574,21 +19703,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -10596,65 +19727,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenXor(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenXor(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -10663,34 +19764,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -10699,9 +19810,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -10713,10 +19824,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.ScreenXor(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -10724,33 +19834,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.LightenXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.LightenXor(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -10770,7 +19880,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -10780,21 +19890,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -10802,65 +19914,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -10869,34 +19951,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(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 amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -10905,9 +19997,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -10919,10 +20011,9 @@ internal static class DefaultPixelBlenders Vector128.Create(Unsafe.Add(ref amountBase, 1))); opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - destinationBase = PorterDuffFunctions.LightenXor(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); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -10930,33 +20021,33 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source, Numerics.Clamp(amount[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))); } /// @@ -10976,7 +20067,7 @@ internal static class DefaultPixelBlenders while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - 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); @@ -10986,21 +20077,23 @@ internal static class DefaultPixelBlenders { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) { + amount = Numerics.Clamp(amount, 0, 1); + if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -11008,65 +20101,35 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - 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); - amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source, amount); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source, amount); } } } - } - - /// - /// 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 BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - amount = Numerics.Clamp(amount, 0, 1); - if (Avx2.IsSupported && destination.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 @@ -11075,34 +20138,44 @@ internal static class DefaultPixelBlenders ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); } if (Numerics.Modulo2(destination.Length) != 0) { // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); } } } /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) { if (Avx2.IsSupported && destination.Length >= 2) { @@ -11111,9 +20184,9 @@ internal static class DefaultPixelBlenders ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref float amountBase = ref MemoryMarshal.GetReference(amount); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); Vector256 vOne = Vector256.Create(1F); while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) @@ -11128,7 +20201,6 @@ internal static class DefaultPixelBlenders destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); destinationBase = ref Unsafe.Add(ref destinationBase, 1); backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); amountBase = ref Unsafe.Add(ref amountBase, 2); } @@ -11136,14 +20208,14 @@ internal static class DefaultPixelBlenders { // 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)); + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } else { for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); } } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index 5b71bb0a64..3b885826b8 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -123,6 +123,44 @@ var blenders = new []{ } } + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + amount = Numerics.Clamp(amount, 0, 1); + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); + } + } + } + /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { @@ -169,6 +207,52 @@ var blenders = new []{ } } } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); + } + } + } } <# diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index 4ed6174386..bad9ae01c1 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -64,6 +64,39 @@ public abstract class PixelBlender PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors[..maxLength], destination, PixelConversionModifiers.Scale); } + /// + /// Blends a row against a constant source color. + /// + /// 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. + /// + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + float amount) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + + using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 2); + Span destinationVectors = buffer.Slice(0, maxLength); + Span backgroundVectors = buffer.Slice(maxLength, maxLength); + + PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); + + this.BlendFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount); + + PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors[..maxLength], destination, PixelConversionModifiers.Scale); + } + /// /// Blends 2 rows together /// @@ -121,6 +154,39 @@ public abstract class PixelBlender PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors[..maxLength], destination, PixelConversionModifiers.Scale); } + /// + /// Blends a row against a constant source color. + /// + /// 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. + /// + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + ReadOnlySpan amount) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); + + using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 2); + Span destinationVectors = buffer.Slice(0, maxLength); + Span backgroundVectors = buffer.Slice(maxLength, maxLength); + + PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); + + this.BlendFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount); + + PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors[..maxLength], destination, PixelConversionModifiers.Scale); + } + /// /// Blend 2 rows together. /// @@ -137,6 +203,22 @@ public abstract class PixelBlender ReadOnlySpan source, float amount); + /// + /// Blend a row against a constant source color. + /// + /// 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. + /// + protected abstract void BlendFunction( + Span destination, + ReadOnlySpan background, + Vector4 source, + float amount); + /// /// Blend 2 rows together. /// @@ -152,4 +234,20 @@ public abstract class PixelBlender ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount); + + /// + /// Blend a row against a constant source color. + /// + /// 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. + /// + protected abstract void BlendFunction( + Span destination, + ReadOnlySpan background, + Vector4 source, + ReadOnlySpan amount); } diff --git a/src/ImageSharp/Primitives/Point.cs b/src/ImageSharp/Primitives/Point.cs index 8627fe980a..99193e3bb0 100644 --- a/src/ImageSharp/Primitives/Point.cs +++ b/src/ImageSharp/Primitives/Point.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp; @@ -232,7 +233,19 @@ public struct Point : IEquatable /// The transformation matrix used. /// The transformed . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point Transform(Point point, Matrix3x2 matrix) => Round(Vector2.Transform(new Vector2(point.X, point.Y), matrix)); + public static PointF Transform(Point point, Matrix3x2 matrix) + => Vector2.Transform(new Vector2(point.X, point.Y), matrix); + + /// + /// Transforms a point by a specified 4x4 matrix, applying a projective transform + /// flattened into 2D space. + /// + /// The point to transform. + /// The transformation matrix used. + /// The transformed . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Transform(Point point, Matrix4x4 matrix) + => TransformUtilities.ProjectiveTransform2D(point.X, point.Y, matrix); /// /// Deconstructs this point into two integers. diff --git a/src/ImageSharp/Primitives/PointF.cs b/src/ImageSharp/Primitives/PointF.cs index 35a506bb41..7979c0af06 100644 --- a/src/ImageSharp/Primitives/PointF.cs +++ b/src/ImageSharp/Primitives/PointF.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp; @@ -246,6 +247,17 @@ public struct PointF : IEquatable [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PointF Transform(PointF point, Matrix3x2 matrix) => Vector2.Transform(point, matrix); + /// + /// Transforms a point by a specified 4x4 matrix, applying a projective transform + /// flattened into 2D space. + /// + /// The point to transform. + /// The transformation matrix used. + /// The transformed . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Transform(PointF point, Matrix4x4 matrix) + => TransformUtilities.ProjectiveTransform2D(point.X, point.Y, matrix); + /// /// Deconstructs this point into two floats. /// diff --git a/src/ImageSharp/Primitives/Rectangle.cs b/src/ImageSharp/Primitives/Rectangle.cs index e2ae5071ef..26a94837fe 100644 --- a/src/ImageSharp/Primitives/Rectangle.cs +++ b/src/ImageSharp/Primitives/Rectangle.cs @@ -260,11 +260,17 @@ public struct Rectangle : IEquatable /// The transformation matrix. /// A transformed rectangle. public static RectangleF Transform(Rectangle rectangle, Matrix3x2 matrix) - { - PointF bottomRight = Point.Transform(new Point(rectangle.Right, rectangle.Bottom), matrix); - PointF topLeft = Point.Transform(rectangle.Location, matrix); - return new RectangleF(topLeft, new SizeF(bottomRight - topLeft)); - } + => RectangleF.Transform(rectangle, matrix); + + /// + /// Transforms a rectangle by the given 4x4 matrix, applying a projective transform + /// flattened into 2D space. + /// + /// The source rectangle. + /// The transformation matrix. + /// A transformed rectangle. + public static RectangleF Transform(Rectangle rectangle, Matrix4x4 matrix) + => RectangleF.Transform(rectangle, matrix); /// /// Converts a to a by performing a truncate operation on all the coordinates. diff --git a/src/ImageSharp/Primitives/RectangleF.cs b/src/ImageSharp/Primitives/RectangleF.cs index 68add77d09..3f41c2d073 100644 --- a/src/ImageSharp/Primitives/RectangleF.cs +++ b/src/ImageSharp/Primitives/RectangleF.cs @@ -236,9 +236,39 @@ public struct RectangleF : IEquatable /// A transformed . public static RectangleF Transform(RectangleF rectangle, Matrix3x2 matrix) { + PointF topLeft = PointF.Transform(rectangle.Location, matrix); + PointF topRight = PointF.Transform(new PointF(rectangle.Right, rectangle.Top), matrix); + PointF bottomLeft = PointF.Transform(new PointF(rectangle.Left, rectangle.Bottom), matrix); PointF bottomRight = PointF.Transform(new PointF(rectangle.Right, rectangle.Bottom), matrix); + + float left = MathF.Min(MathF.Min(topLeft.X, topRight.X), MathF.Min(bottomLeft.X, bottomRight.X)); + float top = MathF.Min(MathF.Min(topLeft.Y, topRight.Y), MathF.Min(bottomLeft.Y, bottomRight.Y)); + float right = MathF.Max(MathF.Max(topLeft.X, topRight.X), MathF.Max(bottomLeft.X, bottomRight.X)); + float bottom = MathF.Max(MathF.Max(topLeft.Y, topRight.Y), MathF.Max(bottomLeft.Y, bottomRight.Y)); + + return FromLTRB(left, top, right, bottom); + } + + /// + /// Transforms a rectangle by the given 4x4 matrix, applying a projective transform + /// flattened into 2D space. + /// + /// The source rectangle. + /// The transformation matrix. + /// A transformed . + public static RectangleF Transform(RectangleF rectangle, Matrix4x4 matrix) + { PointF topLeft = PointF.Transform(rectangle.Location, matrix); - return new RectangleF(topLeft, new SizeF(bottomRight - topLeft)); + PointF topRight = PointF.Transform(new PointF(rectangle.Right, rectangle.Top), matrix); + PointF bottomLeft = PointF.Transform(new PointF(rectangle.Left, rectangle.Bottom), matrix); + PointF bottomRight = PointF.Transform(new PointF(rectangle.Right, rectangle.Bottom), matrix); + + float left = MathF.Min(MathF.Min(topLeft.X, topRight.X), MathF.Min(bottomLeft.X, bottomRight.X)); + float top = MathF.Min(MathF.Min(topLeft.Y, topRight.Y), MathF.Min(bottomLeft.Y, bottomRight.Y)); + float right = MathF.Max(MathF.Max(topLeft.X, topRight.X), MathF.Max(bottomLeft.X, bottomRight.X)); + float bottom = MathF.Max(MathF.Max(topLeft.Y, topRight.Y), MathF.Max(bottomLeft.Y, bottomRight.Y)); + + return FromLTRB(left, top, right, bottom); } /// diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs index c1e11a1d42..84d0d2ea1d 100644 --- a/src/ImageSharp/Processing/AffineTransformBuilder.cs +++ b/src/ImageSharp/Processing/AffineTransformBuilder.cs @@ -349,6 +349,16 @@ public class AffineTransformBuilder internal static SizeF GetTransformedSize(Rectangle sourceRectangle, Matrix3x2 matrix) => TransformUtilities.GetRawTransformedSize(matrix, sourceRectangle.Size); + /// + /// Clears all accumulated transform matrices, resetting the builder to its initial state. + /// + /// The . + public AffineTransformBuilder Clear() + { + this.transformMatrixFactories.Clear(); + return this; + } + private static void CheckDegenerate(Matrix3x2 matrix) { if (TransformUtilities.IsDegenerate(matrix)) diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs index 17bdeadde1..5c7de17100 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs @@ -69,11 +69,17 @@ internal static class TransformUtilities [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 ProjectiveTransform2D(float x, float y, Matrix4x4 matrix) { - // The w component (v4.W) resulting from the transformation can be less than 0 in certain cases, - // such as when the point is transformed behind the camera in a perspective projection. - // However, in many 2D contexts, negative w values are not meaningful and could cause issues - // like flipped or distorted projections. To avoid this, we take the max of w and epsilon to ensure - // we don't divide by a very small or negative number, effectively treating any negative w as epsilon. + // Transforms the 2D point (x, y) as the homogeneous coordinate (x, y, 0, 1) and + // performs the perspective divide (X/W, Y/W) to project back into Cartesian 2D space. + // + // For affine matrices (M14=0, M24=0, M34=0, M44=1) W is always 1 and the divide + // is a no-op, producing the same result as Vector2.Transform(v, Matrix4x4).AsVector2() + // (the approach used by .NET 10+). + // + // For projective matrices (taper, quad distortion) W varies per point and the divide + // is essential for correct perspective mapping. W <= 0 means the point has crossed the + // vanishing line of the projection; clamping to epsilon avoids division by zero or + // negative values that would flip/mirror the output. const float epsilon = 0.0000001F; Vector4 v4 = Vector4.Transform(new Vector4(x, y, 0, 1F), matrix); return new Vector2(v4.X, v4.Y) / MathF.Max(v4.W, epsilon); diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs index e40a307a3d..74da440401 100644 --- a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs +++ b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs @@ -397,6 +397,16 @@ public class ProjectiveTransformBuilder internal static SizeF GetTransformedSize(Rectangle sourceRectangle, Matrix4x4 matrix) => TransformUtilities.GetRawTransformedSize(matrix, sourceRectangle.Size); + /// + /// Clears all accumulated transform matrices, resetting the builder to its initial state. + /// + /// The . + public ProjectiveTransformBuilder Clear() + { + this.transformMatrixFactories.Clear(); + return this; + } + private static void CheckDegenerate(Matrix4x4 matrix) { if (TransformUtilities.IsDegenerate(matrix)) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 6ebe1bf4e0..caa6c507dc 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -572,6 +572,7 @@ public class BmpDecoderTests Assert.IsType(ex.InnerException); } + // https://github.com/SixLabors/ImageSharp/issues/3067 [Fact] public void BmpDecoder_ThrowsException_Issue3067() { @@ -594,4 +595,32 @@ public class BmpDecoderTests using Image image = BmpDecoder.Instance.Decode(DecoderOptions.Default, stream); }); } + + // https://github.com/SixLabors/ImageSharp/issues/3074 + [Fact] + public void BmpDecoder_ThrowsException_Issue3074() + { + // Crafted BMP: pixel data offset = 0x7FFFFFFF, actual file = 35 bytes + byte[] data = + [ + 0x42, 0x4D, // "BM" signature + 0x3A, 0x00, 0x00, 0x00, // file size: 58 + 0x00, 0x00, 0x00, 0x00, // reserved + 0xFF, 0xFF, 0xFF, 0x7F, // pixel offset: 0x7FFFFFFF (2,147,483,647) + 0x28, 0x00, 0x00, 0x00, // DIB header size: 40 + 0x01, 0x00, 0x00, 0x00, // width: 1 + 0x01, 0xFF, 0x00, 0x00, // height: 65281 + 0x01, 0x00, // color planes: 1 + 0x08, 0x00, // bits per pixel: 8 + 0x00, 0x00, 0x00, 0x00, // compression: RGB + 0x00, 0x00, 0x00 // (truncated) + ]; + + using MemoryStream stream = new(data); + + Assert.Throws(() => + { + using Image image = Image.Load(stream); + }); + } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index d047ed2357..36847536b3 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -448,4 +448,13 @@ public partial class JpegDecoderTests [InlineData(TestImages.Jpeg.Issues.Issue2948)] public void Issue2948_No_SOS_Identify_Throws_InvalidImageContentException(string imagePath) => Assert.Throws(() => _ = Image.Identify(TestFile.Create(imagePath).Bytes)); + + [Fact] + public void Issue_3071_Decode_TruncatedJpeg_Throws_InvalidImageContentException() + => Assert.Throws(() => + { + // SOI marker (FF D8) + garbage bytes — only 11 bytes + byte[] data = [0xFF, 0xD8, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30]; + using Image image = Image.Load(data); + }); } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs index 8dfa987488..03dc040186 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -20,40 +20,40 @@ public partial class PngDecoderTests private static readonly byte[] Raw1X1PngIhdrAndpHYs = [ // PNG Identifier - 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, - // IHDR - 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, - 0x00, 0x00, 0x00, + // IHDR + 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, + 0x00, 0x00, 0x00, - // IHDR CRC - 0x90, 0x77, 0x53, 0xDE, + // IHDR CRC + 0x90, 0x77, 0x53, 0xDE, - // pHYS - 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, - 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, + // pHYS + 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, + 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, - // pHYS CRC - 0xC7, 0x6F, 0xA8, 0x64 + // pHYS CRC + 0xC7, 0x6F, 0xA8, 0x64 ]; // Contains the png marker, IDAT and IEND chunks of a 1x1 pixel 32bit png 1 a single black pixel. private static readonly byte[] Raw1X1PngIdatAndIend = [ // IDAT - 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x18, - 0x57, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x01, + 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x18, + 0x57, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x01, - // IDAT CRC - 0x5C, 0xCD, 0xFF, 0x69, + // IDAT CRC + 0x5C, 0xCD, 0xFF, 0x69, - // IEND - 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, + // IEND + 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, - // IEND CRC - 0xAE, 0x42, 0x60, 0x82 + // IEND CRC + 0xAE, 0x42, 0x60, 0x82 ]; [Theory] @@ -70,13 +70,120 @@ public partial class PngDecoderTests WriteChunk(memStream, chunkName); WriteDataChunk(memStream); - ImageFormatException exception = + InvalidImageContentException exception = Assert.Throws(() => PngDecoder.Instance.Decode(DecoderOptions.Default, memStream)); Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message); } } + // https://github.com/SixLabors/ImageSharp/issues/3078 + [Fact] + public void Decode_TruncatedPhysChunk_ExceptionIsThrown() + { + // 24 bytes — PNG signature + truncated pHYs chunk + byte[] payload = Convert.FromHexString( + "89504e470d0a1a0a3030303070485973" + + "3030303030303030"); + + using MemoryStream stream = new(payload); + InvalidImageContentException exception = Assert.Throws(() => Image.Load(stream)); + + Assert.Equal("pHYs chunk is too short", exception.Message); + } + + // https://github.com/SixLabors/ImageSharp/issues/3079 + [Fact] + public void Decode_CompressedTxtChunk_WithTruncatedData_DoesNotThrow() + { + byte[] payload = [137, 80, 78, 71, 13, 10, 26, 10, // PNG signature + 0, 0, 0, 13, // chunk length 13 bytes + 73, 72, 68, 82, // chunk type IHDR + 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, // data + 55, 110, 249, 36, // checksum + 0, 0, 0, 2, // chunk length + 122, 84, 88, 116, // chunk type zTXt + 1, 0, // truncated data + 100, 138, 166, 20, // crc + 0, 0, 0, 10, // chunk length 10 bytes + 73, 68, 65, 84, // chunk type IDAT + 120, 1, 99, 96, 0, 0, 0, 2, 0, 1, // data + 115, 117, 1, 24, // checksum + 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]; // end chunk + + using MemoryStream stream = new(payload); + using Image image = Image.Load(stream); + } + + // https://github.com/SixLabors/ImageSharp/issues/3079 + [Fact] + public void Decode_InternationalText_WithTruncatedData_DoesNotThrow() + { + byte[] payload = [137, 80, 78, 71, 13, 10, 26, 10, // PNG signature + 0, 0, 0, 13, // chunk length 13 bytes + 73, 72, 68, 82, // chunk type IHDR + 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, // data + 55, 110, 249, 36, // checksum + 0, 0, 0, 2, // chunk length + 105, 84, 88, 116, // chunk type iTXt + 1, 0, // truncated data + 225, 200, 214, 33, // crc + 0, 0, 0, 10, // chunk length 10 bytes + 73, 68, 65, 84, // chunk type IDAT + 120, 1, 99, 96, 0, 0, 0, 2, 0, 1, // data + 115, 117, 1, 24, // checksum + 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]; // end chunk + + using MemoryStream stream = new(payload); + using Image image = Image.Load(stream); + } + + // https://github.com/SixLabors/ImageSharp/issues/3079 + [Fact] + public void Decode_InternationalText_WithTruncatedDataAfterLanguageTag_DoesNotThrow() + { + byte[] payload = [137, 80, 78, 71, 13, 10, 26, 10, // PNG signature + 0, 0, 0, 13, // chunk length 13 bytes + 73, 72, 68, 82, // chunk type IHDR + 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, // data + 55, 110, 249, 36, // checksum + 0, 0, 0, 21, // chunk length + 105, 84, 88, 116, // chunk type iTXt + 73, 110, 116, 101, 114, 110, 97, 116, 105, 111, 110, 97, 108, 50, 0, 0, 0, 114, 117, 115, 0, // truncated data after language tag + 225, 200, 214, 33, // crc + 0, 0, 0, 10, // chunk length 10 bytes + 73, 68, 65, 84, // chunk type IDAT + 120, 1, 99, 96, 0, 0, 0, 2, 0, 1, // data + 115, 117, 1, 24, // checksum + 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]; // end chunk + + using MemoryStream stream = new(payload); + using Image image = Image.Load(stream); + } + + [Fact] + public void Decode_tRnsChunk_WithAlphaLengthGreaterColorTableLength_ShouldNotThrowException() + { + byte[] payload = [137, 80, 78, 71, 13, 10, 26, 10, // PNG signature + 0, 0, 0, 13, // chunk length 13 bytes + 73, 72, 68, 82, // chunk type IHDR + 0, 0, 0, 1, 0, 0, 0, 1, 8, 3, 0, 0, 0, // data + 40, 203, 52, 187, // crc + 0, 0, 0, 6, // chunk length 6 bytes + 80, 76, 84, 69, // chunk type palettte + 255, 0, 0, 0, 255, 0, // data + 210, 135, 239, 113, // crc + 0, 0, 0, 18, // chunk length + 116, 82, 78, 83, // chunk type tRns + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, // data + 0, 0, 0, 10, // chunk length + 73, 68, 65, 84, // chunk type data + 120, 156, 99, 96, 0, 0, 0, 2, 0, 1, 72, 175, 164, 113]; // alpha.Length > colorTable.Length + + using MemoryStream stream = new(payload); + using Image image = Image.Load(stream); + } + private static string GetChunkTypeName(uint value) { byte[] data = new byte[4]; diff --git a/tests/ImageSharp.Tests/GraphicsOptionsTests.cs b/tests/ImageSharp.Tests/GraphicsOptionsTests.cs index 0ccb80d3f5..1b87819b68 100644 --- a/tests/ImageSharp.Tests/GraphicsOptionsTests.cs +++ b/tests/ImageSharp.Tests/GraphicsOptionsTests.cs @@ -13,7 +13,7 @@ public class GraphicsOptionsTests private readonly GraphicsOptions cloneGraphicsOptions = new GraphicsOptions().DeepClone(); [Fact] - public void CloneGraphicsOptionsIsNotNull() => Assert.True(this.cloneGraphicsOptions != null); + public void CloneGraphicsOptionsIsNotNull() => Assert.NotNull(this.cloneGraphicsOptions); [Fact] public void DefaultGraphicsOptionsAntialias() @@ -23,35 +23,35 @@ public class GraphicsOptionsTests } [Fact] - public void DefaultGraphicsOptionsAntialiasSuppixelDepth() + public void DefaultGraphicsOptionsAntialiasThreshold() { - const int Expected = 16; - Assert.Equal(Expected, this.newGraphicsOptions.AntialiasSubpixelDepth); - Assert.Equal(Expected, this.cloneGraphicsOptions.AntialiasSubpixelDepth); + const float expected = .5F; + Assert.Equal(expected, this.newGraphicsOptions.AntialiasThreshold); + Assert.Equal(expected, this.cloneGraphicsOptions.AntialiasThreshold); } [Fact] public void DefaultGraphicsOptionsBlendPercentage() { - const float Expected = 1F; - Assert.Equal(Expected, this.newGraphicsOptions.BlendPercentage); - Assert.Equal(Expected, this.cloneGraphicsOptions.BlendPercentage); + const float expected = 1F; + Assert.Equal(expected, this.newGraphicsOptions.BlendPercentage); + Assert.Equal(expected, this.cloneGraphicsOptions.BlendPercentage); } [Fact] public void DefaultGraphicsOptionsColorBlendingMode() { - const PixelColorBlendingMode Expected = PixelColorBlendingMode.Normal; - Assert.Equal(Expected, this.newGraphicsOptions.ColorBlendingMode); - Assert.Equal(Expected, this.cloneGraphicsOptions.ColorBlendingMode); + const PixelColorBlendingMode expected = PixelColorBlendingMode.Normal; + Assert.Equal(expected, this.newGraphicsOptions.ColorBlendingMode); + Assert.Equal(expected, this.cloneGraphicsOptions.ColorBlendingMode); } [Fact] public void DefaultGraphicsOptionsAlphaCompositionMode() { - const PixelAlphaCompositionMode Expected = PixelAlphaCompositionMode.SrcOver; - Assert.Equal(Expected, this.newGraphicsOptions.AlphaCompositionMode); - Assert.Equal(Expected, this.cloneGraphicsOptions.AlphaCompositionMode); + const PixelAlphaCompositionMode expected = PixelAlphaCompositionMode.SrcOver; + Assert.Equal(expected, this.newGraphicsOptions.AlphaCompositionMode); + Assert.Equal(expected, this.cloneGraphicsOptions.AlphaCompositionMode); } [Fact] @@ -61,7 +61,7 @@ public class GraphicsOptionsTests { AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop, Antialias = false, - AntialiasSubpixelDepth = 23, + AntialiasThreshold = .33F, BlendPercentage = .25F, ColorBlendingMode = PixelColorBlendingMode.HardLight, }; @@ -79,7 +79,7 @@ public class GraphicsOptionsTests actual.AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop; actual.Antialias = false; - actual.AntialiasSubpixelDepth = 23; + actual.AntialiasThreshold = .67F; actual.BlendPercentage = .25F; actual.ColorBlendingMode = PixelColorBlendingMode.HardLight; diff --git a/tests/ImageSharp.Tests/Primitives/PointFTests.cs b/tests/ImageSharp.Tests/Primitives/PointFTests.cs index 8b574daf0d..bea0f802fc 100644 --- a/tests/ImageSharp.Tests/Primitives/PointFTests.cs +++ b/tests/ImageSharp.Tests/Primitives/PointFTests.cs @@ -133,6 +133,69 @@ public class PointFTests Assert.Equal(new PointF(30, 30), pout); } + [Fact] + public void TransformMatrix4x4_AffineMatchesMatrix3x2() + { + PointF p = new(13, 17); + Matrix3x2 m3 = Matrix3x2Extensions.CreateRotationDegrees(45, PointF.Empty); + Matrix4x4 m4 = new(m3); + + PointF r3 = PointF.Transform(p, m3); + PointF r4 = PointF.Transform(p, m4); + + Assert.Equal(r3.X, r4.X, ApproximateFloatComparer); + Assert.Equal(r3.Y, r4.Y, ApproximateFloatComparer); + } + + [Fact] + public void TransformMatrix4x4_Identity() + { + PointF p = new(42.5F, -17.3F); + PointF result = PointF.Transform(p, Matrix4x4.Identity); + + Assert.Equal(p.X, result.X, ApproximateFloatComparer); + Assert.Equal(p.Y, result.Y, ApproximateFloatComparer); + } + + [Fact] + public void TransformMatrix4x4_Translation() + { + PointF p = new(10, 20); + Matrix4x4 m = Matrix4x4.CreateTranslation(5, -3, 0); + PointF result = PointF.Transform(p, m); + + Assert.Equal(15F, result.X, ApproximateFloatComparer); + Assert.Equal(17F, result.Y, ApproximateFloatComparer); + } + + [Fact] + public void TransformMatrix4x4_Scale() + { + PointF p = new(10, 20); + Matrix4x4 m = Matrix4x4.CreateScale(2, 3, 1); + PointF result = PointF.Transform(p, m); + + Assert.Equal(20F, result.X, ApproximateFloatComparer); + Assert.Equal(60F, result.Y, ApproximateFloatComparer); + } + + [Fact] + public void TransformMatrix4x4_Projective() + { + // A taper matrix with M14 != 0 produces W != 1, requiring perspective divide. + PointF p = new(100, 50); + Matrix4x4 m = Matrix4x4.Identity; + m.M14 = 0.005F; // perspective component + + PointF result = PointF.Transform(p, m); + + // W = x*M14 + M44 = 100*0.005 + 1 = 1.5 + // X = x*M11 + M41 = 100, Y = y*M22 + M42 = 50 + // result = (100/1.5, 50/1.5) + Assert.Equal(100F / 1.5F, result.X, ApproximateFloatComparer); + Assert.Equal(50F / 1.5F, result.Y, ApproximateFloatComparer); + } + [Theory] [InlineData(float.MaxValue, float.MinValue)] [InlineData(float.MinValue, float.MaxValue)] diff --git a/tests/ImageSharp.Tests/Primitives/PointTests.cs b/tests/ImageSharp.Tests/Primitives/PointTests.cs index 3ad2a83b3d..d2192f53bb 100644 --- a/tests/ImageSharp.Tests/Primitives/PointTests.cs +++ b/tests/ImageSharp.Tests/Primitives/PointTests.cs @@ -159,9 +159,10 @@ public class PointTests Point p = new(13, 17); Matrix3x2 matrix = Matrix3x2Extensions.CreateRotationDegrees(45, Point.Empty); - Point pout = Point.Transform(p, matrix); + PointF pout = Point.Transform(p, matrix); - Assert.Equal(new Point(-3, 21), pout); + Assert.Equal(-2.828427F, pout.X, 4); + Assert.Equal(21.213203F, pout.Y, 4); } [Fact] @@ -170,8 +171,56 @@ public class PointTests Point p = new(13, 17); Matrix3x2 matrix = Matrix3x2Extensions.CreateSkewDegrees(45, 45, Point.Empty); - Point pout = Point.Transform(p, matrix); - Assert.Equal(new Point(30, 30), pout); + PointF pout = Point.Transform(p, matrix); + Assert.Equal(30F, pout.X, 4); + Assert.Equal(30F, pout.Y, 4); + } + + [Fact] + public void TransformMatrix4x4_AffineMatchesMatrix3x2() + { + Point p = new(13, 17); + Matrix3x2 m3 = Matrix3x2Extensions.CreateRotationDegrees(45, Point.Empty); + Matrix4x4 m4 = new(m3); + + PointF r3 = Point.Transform(p, m3); + PointF r4 = Point.Transform(p, m4); + + Assert.Equal(r3, r4); + } + + [Fact] + public void TransformMatrix4x4_Identity() + { + Point p = new(42, -17); + PointF result = Point.Transform(p, Matrix4x4.Identity); + + Assert.Equal((PointF)p, result); + } + + [Fact] + public void TransformMatrix4x4_Translation() + { + Point p = new(10, 20); + Matrix4x4 m = Matrix4x4.CreateTranslation(5, -3, 0); + PointF result = Point.Transform(p, m); + + Assert.Equal(15F, result.X, 4); + Assert.Equal(17F, result.Y, 4); + } + + [Fact] + public void TransformMatrix4x4_Projective() + { + Point p = new(100, 50); + Matrix4x4 m = Matrix4x4.Identity; + m.M14 = 0.005F; + + PointF result = Point.Transform(p, m); + + // W = 100*0.005 + 1 = 1.5 => (100/1.5, 50/1.5) + Assert.Equal(100F / 1.5F, result.X, 4); + Assert.Equal(50F / 1.5F, result.Y, 4); } [Theory] diff --git a/tests/ImageSharp.Tests/Primitives/RectangleFTests.cs b/tests/ImageSharp.Tests/Primitives/RectangleFTests.cs index 4122daaa52..9a6ff0957b 100644 --- a/tests/ImageSharp.Tests/Primitives/RectangleFTests.cs +++ b/tests/ImageSharp.Tests/Primitives/RectangleFTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Globalization; +using System.Numerics; namespace SixLabors.ImageSharp.Tests; @@ -243,6 +244,61 @@ public class RectangleFTests Assert.Equal(expectedRect, r1); } + [Fact] + public void TransformMatrix4x4_AffineMatchesMatrix3x2() + { + RectangleF rect = new(10, 20, 100, 50); + Matrix3x2 m3 = Matrix3x2.CreateTranslation(5, -3); + Matrix4x4 m4 = new(m3); + + RectangleF r3 = RectangleF.Transform(rect, m3); + RectangleF r4 = RectangleF.Transform(rect, m4); + + Assert.Equal(r3, r4); + } + + [Fact] + public void TransformMatrix4x4_Identity() + { + RectangleF rect = new(10, 20, 100, 50); + RectangleF result = RectangleF.Transform(rect, Matrix4x4.Identity); + + Assert.Equal(rect, result); + } + + [Fact] + public void TransformMatrix4x4_Translation() + { + RectangleF rect = new(10, 20, 100, 50); + Matrix4x4 m = Matrix4x4.CreateTranslation(5, -3, 0); + RectangleF result = RectangleF.Transform(rect, m); + + Assert.Equal(new RectangleF(15, 17, 100, 50), result); + } + + [Fact] + public void TransformMatrix4x4_Scale() + { + RectangleF rect = new(10, 20, 100, 50); + Matrix4x4 m = Matrix4x4.CreateScale(2, 3, 1); + RectangleF result = RectangleF.Transform(rect, m); + + Assert.Equal(new RectangleF(20, 60, 200, 150), result); + } + + [Fact] + public void TransformMatrix4x4_RotationReturnsBoundingBox() + { + RectangleF rect = new(10, 20, 100, 50); + Matrix4x4 m = Matrix4x4.CreateRotationZ(MathF.PI / 2F); + RectangleF result = RectangleF.Transform(rect, m); + + Assert.Equal(-70F, result.X, 4); + Assert.Equal(10F, result.Y, 4); + Assert.Equal(50F, result.Width, 4); + Assert.Equal(100F, result.Height, 4); + } + [Fact] public void ToStringTest() { diff --git a/tests/ImageSharp.Tests/Primitives/RectangleTests.cs b/tests/ImageSharp.Tests/Primitives/RectangleTests.cs index 2800852afd..a8ce990789 100644 --- a/tests/ImageSharp.Tests/Primitives/RectangleTests.cs +++ b/tests/ImageSharp.Tests/Primitives/RectangleTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Globalization; +using System.Numerics; namespace SixLabors.ImageSharp.Tests; @@ -294,6 +295,51 @@ public class RectangleTests Assert.Equal(expectedRect, r1); } + [Fact] + public void TransformMatrix4x4_AffineMatchesMatrix3x2() + { + Rectangle rect = new(10, 20, 100, 50); + Matrix3x2 m3 = Matrix3x2.CreateTranslation(5, -3); + Matrix4x4 m4 = new(m3); + + RectangleF r3 = Rectangle.Transform(rect, m3); + RectangleF r4 = Rectangle.Transform(rect, m4); + + Assert.Equal(r3, r4); + } + + [Fact] + public void TransformMatrix4x4_Identity() + { + Rectangle rect = new(10, 20, 100, 50); + RectangleF result = Rectangle.Transform(rect, Matrix4x4.Identity); + + Assert.Equal(new RectangleF(10, 20, 100, 50), result); + } + + [Fact] + public void TransformMatrix4x4_Translation() + { + Rectangle rect = new(10, 20, 100, 50); + Matrix4x4 m = Matrix4x4.CreateTranslation(5, -3, 0); + RectangleF result = Rectangle.Transform(rect, m); + + Assert.Equal(new RectangleF(15, 17, 100, 50), result); + } + + [Fact] + public void TransformMatrix4x4_RotationReturnsBoundingBox() + { + Rectangle rect = new(10, 20, 100, 50); + Matrix4x4 m = Matrix4x4.CreateRotationZ(MathF.PI / 2F); + RectangleF result = Rectangle.Transform(rect, m); + + Assert.Equal(-70F, result.X, 4); + Assert.Equal(10F, result.Y, 4); + Assert.Equal(50F, result.Width, 4); + Assert.Equal(100F, result.Height, 4); + } + [Fact] public void ToStringTest() { diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs index 232571d4d0..d0e4cf5663 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs @@ -58,6 +58,9 @@ public class AffineTransformBuilderTests : TransformBuilderTestBase builder.PrependTranslation(translate); + protected override void ClearBuilder(AffineTransformBuilder builder) + => builder.Clear(); + protected override Vector2 Execute( AffineTransformBuilder builder, Rectangle rectangle, diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs index a0380033fd..dcc6533163 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs @@ -55,6 +55,9 @@ public class ProjectiveTransformBuilderTests : TransformBuilderTestBase builder.PrependRotationRadians(radians, origin); + protected override void ClearBuilder(ProjectiveTransformBuilder builder) + => builder.Clear(); + protected override Vector2 Execute( ProjectiveTransformBuilder builder, Rectangle rectangle, diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs index 73215b1c6f..12f8326fc7 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs @@ -248,6 +248,48 @@ public abstract class TransformBuilderTestBase }); } + [Fact] + public void Clear_ResetsBuilderToIdentity() + { + Size size = new(100, 100); + Rectangle rectangle = new(Point.Empty, size); + Vector2 source = new(10, 20); + + TBuilder builder = this.CreateBuilder(); + + // Apply a transform that changes the point. + this.AppendScale(builder, new SizeF(2, 3)); + this.AppendTranslation(builder, new PointF(50, 50)); + Vector2 transformed = this.Execute(builder, rectangle, source); + Assert.NotEqual(source, transformed, Comparer); + + // Clear and verify the builder produces identity behavior. + this.ClearBuilder(builder); + Vector2 afterClear = this.Execute(builder, rectangle, source); + Assert.Equal(source, afterClear, Comparer); + } + + [Fact] + public void Clear_AllowsReuse() + { + Size size = new(100, 100); + Rectangle rectangle = new(Point.Empty, size); + Vector2 source = new(10, 20); + + TBuilder builder = this.CreateBuilder(); + + // First transform: scale by 2. + this.AppendScale(builder, new SizeF(2, 2)); + Vector2 scaled = this.Execute(builder, rectangle, source); + Assert.Equal(new Vector2(20, 40), scaled, Comparer); + + // Clear and apply a different transform: translate. + this.ClearBuilder(builder); + this.AppendTranslation(builder, new PointF(5, 10)); + Vector2 translated = this.Execute(builder, rectangle, source); + Assert.Equal(new Vector2(15, 30), translated, Comparer); + } + protected abstract TBuilder CreateBuilder(); protected abstract void AppendRotationDegrees(TBuilder builder, float degrees); @@ -282,5 +324,7 @@ public abstract class TransformBuilderTestBase protected abstract void PrependTranslation(TBuilder builder, PointF translate); + protected abstract void ClearBuilder(TBuilder builder); + protected abstract Vector2 Execute(TBuilder builder, Rectangle rectangle, Vector2 sourcePoint); } diff --git a/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs b/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs index 2a7b42f6b4..649da425ec 100644 --- a/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs @@ -6,13 +6,11 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities; public class GraphicsOptionsComparer : IEqualityComparer { public bool Equals(GraphicsOptions x, GraphicsOptions y) - { - return x.AlphaCompositionMode == y.AlphaCompositionMode - && x.Antialias == y.Antialias - && x.AntialiasSubpixelDepth == y.AntialiasSubpixelDepth - && x.BlendPercentage == y.BlendPercentage - && x.ColorBlendingMode == y.ColorBlendingMode; - } + => x.AlphaCompositionMode == y.AlphaCompositionMode + && x.Antialias == y.Antialias + && x.AntialiasThreshold == y.AntialiasThreshold + && x.BlendPercentage == y.BlendPercentage + && x.ColorBlendingMode == y.ColorBlendingMode; public int GetHashCode(GraphicsOptions obj) => obj.GetHashCode(); }