From 034472e0c7b459e2a91f0fadc821915631682090 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 19 Feb 2019 15:03:51 +0100 Subject: [PATCH 01/82] Added base BokehBlurProcessor class, and kernel parameters --- .../Convolution/BokehBlurProcessor.cs | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs new file mode 100644 index 0000000000..47c61b8cc5 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -0,0 +1,108 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Applies bokeh blur processing to the image. + /// + /// The pixel format. + internal class BokehBlurProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// The maximum size of the kernel in either direction. + /// + private readonly int kernelSize; + + /// + /// Initializes a new instance of the class. + /// + public BokehBlurProcessor() + { + } + + /// + /// Gets the Radius + /// + public int Radius { get; } + + /// + /// Gets the horizontal gradient operator. + /// + public DenseMatrix KernelX { get; } + + /// + /// Gets the vertical gradient operator. + /// + public DenseMatrix KernelY { get; } + + /// + /// Gets the kernel scales to adjust the component values in each kernel + /// + private static IReadOnlyList KernelScales { get; } = new[] { 1.4, 1.2, 1.2, 1.2, 1.2, 1.2 }; + + /// + /// Gets the available bokeh blur kernel parameters + /// + private static IReadOnlyList KernelParameters { get; } = new[] + { + // 1 component + new[,] { { 0.862325, 1.624835, 0.767583, 1.862321 } }, + + // 2 components + new[,] + { + { 0.886528, 5.268909, 0.411259, -0.548794 }, + { 1.960518, 1.558213, 0.513282, 4.56111 } + }, + + // 3 components + new[,] + { + { 2.17649, 5.043495, 1.621035, -2.105439 }, + { 1.019306, 9.027613, -0.28086, -0.162882 }, + { 2.81511, 1.597273, -0.366471, 10.300301 } + }, + + // 4 components + new[,] + { + { 4.338459, 1.553635, -5.767909, 46.164397 }, + { 3.839993, 4.693183, 9.795391, -15.227561 }, + { 2.791880, 8.178137, -3.048324, 0.302959 }, + { 1.342190, 12.328289, 0.010001, 0.244650 } + }, + + // 5 components + new[,] + { + { 4.892608, 1.685979, -22.356787, 85.91246 }, + { 4.71187, 4.998496, 35.918936, -28.875618 }, + { 4.052795, 8.244168, -13.212253, -1.578428 }, + { 2.929212, 11.900859, 0.507991, 1.816328 }, + { 1.512961, 16.116382, 0.138051, -0.01 } + }, + + // 6 components + new[,] + { + { 5.143778, 2.079813, -82.326596, 111.231024 }, + { 5.612426, 6.153387, 113.878661, 58.004879 }, + { 5.982921, 9.802895, 39.479083, -162.028887 }, + { 6.505167, 11.059237, -71.286026, 95.027069 }, + { 3.869579, 14.81052, 1.405746, -3.704914 }, + { 2.201904, 19.032909, -0.152784, -0.107988 } + } + }; + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) => throw new NotImplementedException(); + } +} From cdd004de9683f338d90008c4d996ed30228e9642 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 19 Feb 2019 15:25:31 +0100 Subject: [PATCH 02/82] Added method to calculate the kernel parameters --- .../Convolution/BokehBlurProcessor.cs | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 47c61b8cc5..6eddb5ef20 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -21,11 +21,17 @@ internal class BokehBlurProcessor : ImageProcessor /// private readonly int kernelSize; + /// + /// The number of components to use when applying the bokeh blur + /// + private readonly int componentsCount; + /// /// Initializes a new instance of the class. /// - public BokehBlurProcessor() + public BokehBlurProcessor(int components = 2) { + this.componentsCount = components; } /// @@ -102,6 +108,30 @@ public BokehBlurProcessor() } }; + /// + /// Gets the kernel parameters and scaling factor for the current count value in the current instance + /// + private (IReadOnlyList> Components, double Scale) GetParameters() + { + // Prepare the kernel components + int index = Math.Max(0, Math.Min(this.componentsCount - 1, KernelParameters.Count)); + double[,] parameters = KernelParameters[index]; + var mapping = new IReadOnlyDictionary[parameters.GetLength(0)]; + for (int i = 0; i < parameters.GetLength(0); i++) + { + mapping[i] = new Dictionary + { + ['a'] = parameters[i, 0], + ['b'] = parameters[i, 1], + ['A'] = parameters[i, 2], + ['B'] = parameters[i, 3] + }; + } + + // Return the components and the adjustment scale + return (mapping, KernelScales[index]); + } + /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) => throw new NotImplementedException(); } From 0bcd24459d17a79b342c7918009d9d01b6a66ccc Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 19 Feb 2019 15:59:47 +0100 Subject: [PATCH 03/82] Switched to float, added method to create the 1D kernels --- .../Convolution/BokehBlurProcessor.cs | 125 ++++++++++++------ 1 file changed, 86 insertions(+), 39 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 6eddb5ef20..512985548a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -3,8 +3,9 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Numerics; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution @@ -27,99 +28,117 @@ internal class BokehBlurProcessor : ImageProcessor private readonly int componentsCount; /// - /// Initializes a new instance of the class. + /// The kernel components to use for the current instance /// - public BokehBlurProcessor(int components = 2) - { - this.componentsCount = components; - } + private readonly IReadOnlyList> kernelComponents; /// - /// Gets the Radius + /// The scaling factor for kernel values /// - public int Radius { get; } + private readonly float kernelsScale; /// - /// Gets the horizontal gradient operator. + /// The complex kernels to use to apply the blur for the current instance /// - public DenseMatrix KernelX { get; } + private readonly IReadOnlyList complexKernels; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The 'radius' value representing the size of the area to sample. + /// + /// + /// The number of components to use to approximate the original 2D bokeh blur convolution kernel. + /// + public BokehBlurProcessor(int radius = 32, int components = 2) + { + this.Radius = radius; + this.kernelSize = (radius * 2) + 1; + this.componentsCount = components; + + (this.kernelComponents, this.kernelsScale) = this.GetParameters(); + this.complexKernels = ( + from component in this.kernelComponents + select this.CreateComplex1DKernel(component['a'], component['b'])).ToArray(); + } /// - /// Gets the vertical gradient operator. + /// Gets the Radius /// - public DenseMatrix KernelY { get; } + public int Radius { get; } /// /// Gets the kernel scales to adjust the component values in each kernel /// - private static IReadOnlyList KernelScales { get; } = new[] { 1.4, 1.2, 1.2, 1.2, 1.2, 1.2 }; + private static IReadOnlyList KernelScales { get; } = new[] { 1.4f, 1.2f, 1.2f, 1.2f, 1.2f, 1.2f }; /// /// Gets the available bokeh blur kernel parameters /// - private static IReadOnlyList KernelParameters { get; } = new[] + private static IReadOnlyList KernelParameters { get; } = new[] { // 1 component - new[,] { { 0.862325, 1.624835, 0.767583, 1.862321 } }, + new[,] { { 0.862325f, 1.624835f, 0.767583f, 1.862321f } }, // 2 components new[,] { - { 0.886528, 5.268909, 0.411259, -0.548794 }, - { 1.960518, 1.558213, 0.513282, 4.56111 } + { 0.886528f, 5.268909f, 0.411259f, -0.548794f }, + { 1.960518f, 1.558213f, 0.513282f, 4.56111f } }, // 3 components new[,] { - { 2.17649, 5.043495, 1.621035, -2.105439 }, - { 1.019306, 9.027613, -0.28086, -0.162882 }, - { 2.81511, 1.597273, -0.366471, 10.300301 } + { 2.17649f, 5.043495f, 1.621035f, -2.105439f }, + { 1.019306f, 9.027613f, -0.28086f, -0.162882f }, + { 2.81511f, 1.597273f, -0.366471f, 10.300301f } }, // 4 components new[,] { - { 4.338459, 1.553635, -5.767909, 46.164397 }, - { 3.839993, 4.693183, 9.795391, -15.227561 }, - { 2.791880, 8.178137, -3.048324, 0.302959 }, - { 1.342190, 12.328289, 0.010001, 0.244650 } + { 4.338459f, 1.553635f, -5.767909f, 46.164397f }, + { 3.839993f, 4.693183f, 9.795391f, -15.227561f }, + { 2.791880f, 8.178137f, -3.048324f, 0.302959f }, + { 1.342190f, 12.328289f, 0.010001f, 0.244650f } }, // 5 components new[,] { - { 4.892608, 1.685979, -22.356787, 85.91246 }, - { 4.71187, 4.998496, 35.918936, -28.875618 }, - { 4.052795, 8.244168, -13.212253, -1.578428 }, - { 2.929212, 11.900859, 0.507991, 1.816328 }, - { 1.512961, 16.116382, 0.138051, -0.01 } + { 4.892608f, 1.685979f, -22.356787f, 85.91246f }, + { 4.71187f, 4.998496f, 35.918936f, -28.875618f }, + { 4.052795f, 8.244168f, -13.212253f, -1.578428f }, + { 2.929212f, 11.900859f, 0.507991f, 1.816328f }, + { 1.512961f, 16.116382f, 0.138051f, -0.01f } }, // 6 components new[,] { - { 5.143778, 2.079813, -82.326596, 111.231024 }, - { 5.612426, 6.153387, 113.878661, 58.004879 }, - { 5.982921, 9.802895, 39.479083, -162.028887 }, - { 6.505167, 11.059237, -71.286026, 95.027069 }, - { 3.869579, 14.81052, 1.405746, -3.704914 }, - { 2.201904, 19.032909, -0.152784, -0.107988 } + { 5.143778f, 2.079813f, -82.326596f, 111.231024f }, + { 5.612426f, 6.153387f, 113.878661f, 58.004879f }, + { 5.982921f, 9.802895f, 39.479083f, -162.028887f }, + { 6.505167f, 11.059237f, -71.286026f, 95.027069f }, + { 3.869579f, 14.81052f, 1.405746f, -3.704914f }, + { 2.201904f, 19.032909f, -0.152784f, -0.107988f } } }; /// /// Gets the kernel parameters and scaling factor for the current count value in the current instance /// - private (IReadOnlyList> Components, double Scale) GetParameters() + private (IReadOnlyList> Components, float Scale) GetParameters() { // Prepare the kernel components int index = Math.Max(0, Math.Min(this.componentsCount - 1, KernelParameters.Count)); - double[,] parameters = KernelParameters[index]; - var mapping = new IReadOnlyDictionary[parameters.GetLength(0)]; + float[,] parameters = KernelParameters[index]; + var mapping = new IReadOnlyDictionary[parameters.GetLength(0)]; for (int i = 0; i < parameters.GetLength(0); i++) { - mapping[i] = new Dictionary + mapping[i] = new Dictionary { ['a'] = parameters[i, 0], ['b'] = parameters[i, 1], @@ -132,6 +151,34 @@ public BokehBlurProcessor(int components = 2) return (mapping, KernelScales[index]); } + /// + /// Creates a complex 1D kernel with the specified parameters + /// + /// The exponential parameter for each complex component + /// The angle component for each complex component + private Complex[] CreateComplex1DKernel(float a, float b) + { + // Precompute the range values + float[] ax = Enumerable.Range(-this.Radius, this.Radius + 1).Select( + i => + { + float value = i * this.kernelsScale * (1f / this.Radius); + return value * value; + }).ToArray(); + + // Compute the complex kernels + var kernel = new Complex[this.kernelSize]; + for (int i = 0; i < this.kernelSize; i++) + { + double + real = Math.Exp(-a * ax[i]) * Math.Cos(b * ax[i]), + imaginary = Math.Exp(-a * ax[i]) * Math.Sin(b * ax[i]); + kernel[i] = new Complex(real, imaginary); + } + + return kernel; + } + /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) => throw new NotImplementedException(); } From 3dbf5e541755f3418ccb1ff15f3cc4bcc60dd020 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 19 Feb 2019 16:20:54 +0100 Subject: [PATCH 04/82] Added complex kernels normalization --- .../Convolution/BokehBlurProcessor.cs | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 512985548a..344d2717b8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -30,7 +30,7 @@ internal class BokehBlurProcessor : ImageProcessor /// /// The kernel components to use for the current instance /// - private readonly IReadOnlyList> kernelComponents; + private readonly IReadOnlyList> kernelParameters; /// /// The scaling factor for kernel values @@ -57,10 +57,11 @@ public BokehBlurProcessor(int radius = 32, int components = 2) this.kernelSize = (radius * 2) + 1; this.componentsCount = components; - (this.kernelComponents, this.kernelsScale) = this.GetParameters(); + (this.kernelParameters, this.kernelsScale) = this.GetParameters(); this.complexKernels = ( - from component in this.kernelComponents + from component in this.kernelParameters select this.CreateComplex1DKernel(component['a'], component['b'])).ToArray(); + this.NormalizeKernels(); } /// @@ -76,7 +77,7 @@ from component in this.kernelComponents /// /// Gets the available bokeh blur kernel parameters /// - private static IReadOnlyList KernelParameters { get; } = new[] + private static IReadOnlyList KernelComponents { get; } = new[] { // 1 component new[,] { { 0.862325f, 1.624835f, 0.767583f, 1.862321f } }, @@ -130,11 +131,11 @@ from component in this.kernelComponents /// /// Gets the kernel parameters and scaling factor for the current count value in the current instance /// - private (IReadOnlyList> Components, float Scale) GetParameters() + private (IReadOnlyList> Parameters, float Scale) GetParameters() { // Prepare the kernel components - int index = Math.Max(0, Math.Min(this.componentsCount - 1, KernelParameters.Count)); - float[,] parameters = KernelParameters[index]; + int index = Math.Max(0, Math.Min(this.componentsCount - 1, KernelComponents.Count)); + float[,] parameters = KernelComponents[index]; var mapping = new IReadOnlyDictionary[parameters.GetLength(0)]; for (int i = 0; i < parameters.GetLength(0); i++) { @@ -179,6 +180,37 @@ private Complex[] CreateComplex1DKernel(float a, float b) return kernel; } + /// + /// Normalizes the kernels with respect to A * real + B * imaginary + /// + private void NormalizeKernels() + { + // Calculate the complex weighted sum + double total = 0; + foreach ((Complex[] kernel, IReadOnlyDictionary param) in this.complexKernels.Zip(this.kernelParameters, (k, p) => (k, p))) + { + for (int i = 0; i < kernel.Length; i++) + { + for (int j = 0; j < kernel.Length; j++) + { + total += + (param['A'] * ((kernel[i].Real * kernel[j].Real) - (kernel[i].Imaginary * kernel[j].Imaginary))) + + (param['B'] * ((kernel[i].Real * kernel[j].Imaginary) + (kernel[i].Imaginary * kernel[j].Real))); + } + } + } + + // Normalize the kernels + float scalar = (float)(1f / Math.Sqrt(total)); + foreach (Complex[] kernel in this.complexKernels) + { + for (int i = 0; i < kernel.Length; i++) + { + kernel[i] = kernel[i] * scalar; + } + } + } + /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) => throw new NotImplementedException(); } From c21d35be80df30e4bc81f9bc7963a345abbc545b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 19 Feb 2019 16:31:50 +0100 Subject: [PATCH 05/82] Added BokehBlurExtensions class --- .../Processing/BokehBlurExtensions.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/ImageSharp/Processing/BokehBlurExtensions.cs diff --git a/src/ImageSharp/Processing/BokehBlurExtensions.cs b/src/ImageSharp/Processing/BokehBlurExtensions.cs new file mode 100644 index 0000000000..9d7dd65f43 --- /dev/null +++ b/src/ImageSharp/Processing/BokehBlurExtensions.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Convolution; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Adds bokeh blurring extensions to the type. + /// + public static class BokehBlurExtensions + { + /// + /// Applies a bokeh blur to the image. + /// + /// The pixel format. + /// The image this method extends. + /// The . + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source) + where TPixel : struct, IPixel + => source.ApplyProcessor(new BokehBlurProcessor()); + + /// + /// Applies a bokeh blur to the image. + /// + /// The pixel format. + /// The image this method extends. + /// The 'radius' value representing the size of the area to sample. + /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. + /// The . + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components) + where TPixel : struct, IPixel + => source.ApplyProcessor(new BokehBlurProcessor(radius, components)); + + /// + /// Applies a bokeh blur to the image. + /// + /// The pixel format. + /// The image this method extends. + /// The 'radius' value representing the size of the area to sample. + /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, Rectangle rectangle) + where TPixel : struct, IPixel + => source.ApplyProcessor(new BokehBlurProcessor(radius, components), rectangle); + } +} From e0c60cc9845a374220be66a753326379e57c501d Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 20 Feb 2019 12:46:32 +0100 Subject: [PATCH 06/82] Added the Complex64 struct type --- src/ImageSharp/Primitives/Complex64.cs | 51 ++++++++++++++++++++++++++ src/ImageSharp/Primitives/Rational.cs | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp/Primitives/Complex64.cs diff --git a/src/ImageSharp/Primitives/Complex64.cs b/src/ImageSharp/Primitives/Complex64.cs new file mode 100644 index 0000000000..02469d2dea --- /dev/null +++ b/src/ImageSharp/Primitives/Complex64.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Primitives +{ + /// + /// Represents a complex number, where the real and imaginary parts are stored as values. + /// + /// + /// This is a more efficient version of the type. + /// + internal readonly struct Complex64 + { + /// + /// The real part of the complex number + /// + public readonly float Real; + + /// + /// The imaginary part of the complex number + /// + public readonly float Imaginary; + + /// + /// Initializes a new instance of the struct. + /// + /// The real part in the complex number. + /// The imaginary part in the complex number. + public Complex64(float real, float imaginary) + { + this.Real = real; + this.Imaginary = imaginary; + } + + /// + /// Performs the multiplication operation between a intance and a scalar. + /// + /// The value to multiply. + /// The scalar to use to multiply the value. + /// The result + public static Complex64 operator *(Complex64 value, float scalar) => new Complex64(value.Real * scalar, value.Imaginary * scalar); + + /// + /// Performs the addition operation between two intances. + /// + /// The first value to sum. + /// The second value to sum. + /// The result + public static Complex64 operator +(Complex64 left, Complex64 right) => new Complex64(left.Real + right.Real, left.Imaginary + right.Imaginary); + } +} diff --git a/src/ImageSharp/Primitives/Rational.cs b/src/ImageSharp/Primitives/Rational.cs index b598f0e02f..6b134bbfd7 100644 --- a/src/ImageSharp/Primitives/Rational.cs +++ b/src/ImageSharp/Primitives/Rational.cs @@ -102,7 +102,7 @@ public Rational(double value, bool bestPrecision) /// Determines whether the specified instances are not considered equal. /// /// The first to compare. - /// The second to compare. + /// The second to compare. /// The public static bool operator !=(Rational left, Rational right) { From b42b9f74c5a47bfd885c0c229c62bb1e65dd21dc Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 20 Feb 2019 12:49:48 +0100 Subject: [PATCH 07/82] Switched to Complex64 in the BokehBlurProcessor --- .../Convolution/BokehBlurProcessor.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 344d2717b8..7aa298e9c0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Numerics; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution @@ -40,7 +40,7 @@ internal class BokehBlurProcessor : ImageProcessor /// /// The complex kernels to use to apply the blur for the current instance /// - private readonly IReadOnlyList complexKernels; + private readonly IReadOnlyList complexKernels; /// /// Initializes a new instance of the class. @@ -157,7 +157,7 @@ from component in this.kernelParameters /// /// The exponential parameter for each complex component /// The angle component for each complex component - private Complex[] CreateComplex1DKernel(float a, float b) + private Complex64[] CreateComplex1DKernel(float a, float b) { // Precompute the range values float[] ax = Enumerable.Range(-this.Radius, this.Radius + 1).Select( @@ -168,13 +168,13 @@ private Complex[] CreateComplex1DKernel(float a, float b) }).ToArray(); // Compute the complex kernels - var kernel = new Complex[this.kernelSize]; + var kernel = new Complex64[this.kernelSize]; for (int i = 0; i < this.kernelSize; i++) { - double - real = Math.Exp(-a * ax[i]) * Math.Cos(b * ax[i]), - imaginary = Math.Exp(-a * ax[i]) * Math.Sin(b * ax[i]); - kernel[i] = new Complex(real, imaginary); + float + real = (float)(Math.Exp(-a * ax[i]) * Math.Cos(b * ax[i])), + imaginary = (float)(Math.Exp(-a * ax[i]) * Math.Sin(b * ax[i])); + kernel[i] = new Complex64(real, imaginary); } return kernel; @@ -187,7 +187,7 @@ private void NormalizeKernels() { // Calculate the complex weighted sum double total = 0; - foreach ((Complex[] kernel, IReadOnlyDictionary param) in this.complexKernels.Zip(this.kernelParameters, (k, p) => (k, p))) + foreach ((Complex64[] kernel, IReadOnlyDictionary param) in this.complexKernels.Zip(this.kernelParameters, (k, p) => (k, p))) { for (int i = 0; i < kernel.Length; i++) { @@ -202,7 +202,7 @@ private void NormalizeKernels() // Normalize the kernels float scalar = (float)(1f / Math.Sqrt(total)); - foreach (Complex[] kernel in this.complexKernels) + foreach (Complex64[] kernel in this.complexKernels) { for (int i = 0; i < kernel.Length; i++) { From 177a40c56b6c7edba9bc07a6224865568062d9cc Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 20 Feb 2019 13:00:35 +0100 Subject: [PATCH 08/82] Added caching system for the bokeh processor parameters --- .../Convolution/BokehBlurProcessor.cs | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 7aa298e9c0..befc9eeec2 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -42,6 +42,12 @@ internal class BokehBlurProcessor : ImageProcessor /// private readonly IReadOnlyList complexKernels; + /// + /// The mapping of initialized complex kernels and parameters, to speed up the initialization of new instances + /// + private static readonly Dictionary<(int, int), (IReadOnlyList>, float, IReadOnlyList)> Cache = + new Dictionary<(int, int), (IReadOnlyList>, float, IReadOnlyList)>(); + /// /// Initializes a new instance of the class. /// @@ -57,11 +63,25 @@ public BokehBlurProcessor(int radius = 32, int components = 2) this.kernelSize = (radius * 2) + 1; this.componentsCount = components; - (this.kernelParameters, this.kernelsScale) = this.GetParameters(); - this.complexKernels = ( - from component in this.kernelParameters - select this.CreateComplex1DKernel(component['a'], component['b'])).ToArray(); - this.NormalizeKernels(); + // Reuse the initialized values from the cache, if possible + if (Cache.TryGetValue((radius, components), out (IReadOnlyList>, float, IReadOnlyList) info)) + { + this.kernelParameters = info.Item1; + this.kernelsScale = info.Item2; + this.complexKernels = info.Item3; + } + else + { + // Initialize the complex kernels and parameters with the current arguments + (this.kernelParameters, this.kernelsScale) = this.GetParameters(); + this.complexKernels = ( + from component in this.kernelParameters + select this.CreateComplex1DKernel(component['a'], component['b'])).ToArray(); + this.NormalizeKernels(); + + // Store them in the cache for future use + Cache.Add((radius, components), (this.kernelParameters, this.kernelsScale, this.complexKernels)); + } } /// From 0716cede51474bb6d785ae89c2273f79f310cd55 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 20 Feb 2019 13:20:20 +0100 Subject: [PATCH 09/82] Added WeightedSum method to the Complex64 type --- src/ImageSharp/Primitives/Complex64.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/ImageSharp/Primitives/Complex64.cs b/src/ImageSharp/Primitives/Complex64.cs index 02469d2dea..6219380f70 100644 --- a/src/ImageSharp/Primitives/Complex64.cs +++ b/src/ImageSharp/Primitives/Complex64.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System.Runtime.CompilerServices; + namespace SixLabors.ImageSharp.Primitives { /// @@ -38,6 +40,7 @@ public Complex64(float real, float imaginary) /// The value to multiply. /// The scalar to use to multiply the value. /// The result + [MethodImpl(InliningOptions.ShortMethod)] public static Complex64 operator *(Complex64 value, float scalar) => new Complex64(value.Real * scalar, value.Imaginary * scalar); /// @@ -46,6 +49,16 @@ public Complex64(float real, float imaginary) /// The first value to sum. /// The second value to sum. /// The result + [MethodImpl(InliningOptions.ShortMethod)] public static Complex64 operator +(Complex64 left, Complex64 right) => new Complex64(left.Real + right.Real, left.Imaginary + right.Imaginary); + + /// + /// Performs a weighted sum on the current instance according to the given parameters + /// + /// The 'a' parameter, for the real component + /// The 'b' parameter, for the imaginary component + /// The resulting value + [MethodImpl(InliningOptions.ShortMethod)] + public float WeightedSum(float a, float b) => (this.Real * a) + (this.Imaginary * b); } } From 47721ce3784fcf5ca62b5792bc5d31c13f0e1816 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 21 Feb 2019 13:27:56 +0100 Subject: [PATCH 10/82] Added IEquatable interface to the Complex64 type --- src/ImageSharp/Primitives/Complex64.cs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Primitives/Complex64.cs b/src/ImageSharp/Primitives/Complex64.cs index 6219380f70..212435527a 100644 --- a/src/ImageSharp/Primitives/Complex64.cs +++ b/src/ImageSharp/Primitives/Complex64.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Primitives @@ -11,7 +12,7 @@ namespace SixLabors.ImageSharp.Primitives /// /// This is a more efficient version of the type. /// - internal readonly struct Complex64 + internal readonly struct Complex64 : IEquatable { /// /// The real part of the complex number @@ -60,5 +61,23 @@ public Complex64(float real, float imaginary) /// The resulting value [MethodImpl(InliningOptions.ShortMethod)] public float WeightedSum(float a, float b) => (this.Real * a) + (this.Imaginary * b); + + /// + public bool Equals(Complex64 other) + { + return this.Real.Equals(other.Real) && this.Imaginary.Equals(other.Imaginary); + } + + /// + public override bool Equals(object obj) => obj is Complex64 other && this.Equals(other); + + /// + public override int GetHashCode() + { + unchecked + { + return (this.Real.GetHashCode() * 397) ^ this.Imaginary.GetHashCode(); + } + } } } From 8468a2f1bb6e17ceb1e73a7c85f42930a8851a6c Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 21 Feb 2019 15:54:26 +0100 Subject: [PATCH 11/82] New complex types added --- src/ImageSharp/Primitives/Complex64.cs | 9 +++++++++ src/ImageSharp/Primitives/ComplexVector4.cs | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/ImageSharp/Primitives/ComplexVector4.cs diff --git a/src/ImageSharp/Primitives/Complex64.cs b/src/ImageSharp/Primitives/Complex64.cs index 212435527a..890ff6228e 100644 --- a/src/ImageSharp/Primitives/Complex64.cs +++ b/src/ImageSharp/Primitives/Complex64.cs @@ -53,6 +53,15 @@ public Complex64(float real, float imaginary) [MethodImpl(InliningOptions.ShortMethod)] public static Complex64 operator +(Complex64 left, Complex64 right) => new Complex64(left.Real + right.Real, left.Imaginary + right.Imaginary); + /// + /// Performs the multiplication operation between two intances. + /// + /// The first value to multiply. + /// The second value to multiply. + /// The result + [MethodImpl(InliningOptions.ShortMethod)] + public static Complex64 operator *(Complex64 left, Complex64 right) => new Complex64((left.Real * right.Real) - (left.Imaginary * right.Imaginary), (left.Real * right.Imaginary) + (left.Imaginary * right.Real)); + /// /// Performs a weighted sum on the current instance according to the given parameters /// diff --git a/src/ImageSharp/Primitives/ComplexVector4.cs b/src/ImageSharp/Primitives/ComplexVector4.cs new file mode 100644 index 0000000000..ead7234f47 --- /dev/null +++ b/src/ImageSharp/Primitives/ComplexVector4.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Primitives +{ + /// + /// A vector with 4 values of type . + /// + internal struct ComplexVector4 + { + public Complex64 X; + public Complex64 Y; + public Complex64 Z; + public Complex64 W; + } +} From 2e7fb74929087d45d1ea5efc651cff53dd28f4ce Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 21 Feb 2019 15:54:43 +0100 Subject: [PATCH 12/82] Added method to reshape a DenseMatrix with no copies --- src/ImageSharp/Primitives/DenseMatrix{T}.cs | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/ImageSharp/Primitives/DenseMatrix{T}.cs b/src/ImageSharp/Primitives/DenseMatrix{T}.cs index 170292e29e..11ba5a96f4 100644 --- a/src/ImageSharp/Primitives/DenseMatrix{T}.cs +++ b/src/ImageSharp/Primitives/DenseMatrix{T}.cs @@ -51,6 +51,25 @@ public DenseMatrix(int length) { } + /// + /// Initializes a new instance of the struct. + /// + /// The array to provide access to. + /// The number of columns. + /// The number of rows. + private DenseMatrix(T[] data, int columns, int rows) + { + Guard.MustBeGreaterThan(columns, 0, nameof(columns)); + Guard.MustBeGreaterThan(rows, 0, nameof(rows)); + Guard.MustBeLessThanOrEqualTo(rows * columns, data.Length, nameof(data)); + + this.Rows = rows; + this.Columns = columns; + this.Size = new Size(columns, rows); + this.Count = columns * rows; + this.Data = data; + } + /// /// Initializes a new instance of the struct. /// @@ -175,6 +194,18 @@ public DenseMatrix Transpose() return result; } + /// + /// Reshapes the current memory, without performing copy operations. + /// + /// The new width of the instance to create. + /// The new height of the instance to create. + [MethodImpl(InliningOptions.ShortMethod)] + internal DenseMatrix Reshape(int columns, int rows) + { + SixLabors.DebugGuard.MustBeLessThanOrEqualTo(columns * rows, this.Columns * this.Rows, nameof(columns)); + return new DenseMatrix(this.Data, columns, rows); + } + /// /// Fills the matrix with the given value /// From bd8c2e362cbe415c800dc032b9615b31ee3c6679 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 21 Feb 2019 15:58:03 +0100 Subject: [PATCH 13/82] Added bokeh convolution first pass (WIP) --- .../Processors/Drawing/DrawImageProcessor.cs | 196 +- .../Processors/Drawing/FillProcessor.cs | 88 +- .../Processing/TextGraphicsOptions.cs | 324 +- .../Common/Helpers/DenseMatrixUtils.cs | 88 +- src/ImageSharp/Configuration.cs | 326 +- src/ImageSharp/Formats/ImageFormatManager.cs | 402 +- .../Formats/Jpeg/5116.DCT_Filter.pdf | Bin 218450 -> 218384 bytes src/ImageSharp/Formats/Jpeg/itu-t81.pdf | Bin 1058883 -> 1057800 bytes src/ImageSharp/GraphicsOptions.cs | 352 +- .../PixelFormats/PixelAlphaCompositionMode.cs | 6 +- .../DefaultPixelBlenders.Generated.cs | 7618 ++++++++--------- .../DefaultPixelBlenders.Generated.tt | 226 +- .../PorterDuffFunctions.Generated.cs | 4322 +++++----- .../PorterDuffFunctions.Generated.tt | 364 +- .../PixelBlenders/PorterDuffFunctions.cs | 474 +- .../PixelFormats/PixelBlender{TPixel}.cs | 338 +- .../PixelOperations{TPixel}.PixelBenders.cs | 432 +- .../Convolution/BokehBlurProcessor.cs | 101 +- .../Drawing/SolidFillBlendedShapesTests.cs | 352 +- .../Formats/ImageFormatManagerTests.cs | 292 +- .../PorterDuffCompositorTests.cs | 110 +- .../PixelOperationsTests.Blender.cs | 110 +- 22 files changed, 8338 insertions(+), 8183 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs index 54b7315b21..889b7b5674 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -1,99 +1,99 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Buffers; -using System.Threading.Tasks; - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors.Drawing -{ - /// - /// Combines two images together by blending the pixels. - /// - public class DrawImageProcessor : IImageProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// The image to blend. - /// The location to draw the blended image. - /// The blending mode to use when drawing the image. - /// The Alpha blending mode to use when drawing the image. - /// The opacity of the image to blend. - public DrawImageProcessor( - Image image, - Point location, - PixelColorBlendingMode colorBlendingMode, - PixelAlphaCompositionMode alphaCompositionMode, - float opacity) - { - this.Image = image; - this.Location = location; - this.ColorBlendingMode = colorBlendingMode; - this.AlphaCompositionMode = alphaCompositionMode; - this.Opacity = opacity; - } - - /// - /// Gets the image to blend. - /// - public Image Image { get; } - - /// - /// Gets the location to draw the blended image. - /// - public Point Location { get; } - - /// - /// Gets the blending mode to use when drawing the image. - /// - public PixelColorBlendingMode ColorBlendingMode { get; } - - /// - /// Gets the Alpha blending mode to use when drawing the image. - /// - public PixelAlphaCompositionMode AlphaCompositionMode { get; } - - /// - /// Gets the opacity of the image to blend. - /// - public float Opacity { get; } - - /// - public IImageProcessor CreatePixelSpecificProcessor() - where TPixelBg : struct, IPixel - { - var visitor = new ProcessorFactoryVisitor(this); - this.Image.AcceptVisitor(visitor); - return visitor.Result; - } - - private class ProcessorFactoryVisitor : IImageVisitor - where TPixelBg : struct, IPixel - { - private readonly DrawImageProcessor definition; - - public ProcessorFactoryVisitor(DrawImageProcessor definition) - { - this.definition = definition; - } - - public IImageProcessor Result { get; private set; } - - public void Visit(Image image) - where TPixelFg : struct, IPixel - { - this.Result = new DrawImageProcessor( - image, - this.definition.Location, - this.definition.ColorBlendingMode, - this.definition.AlphaCompositionMode, - this.definition.Opacity); - } - } - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Buffers; +using System.Threading.Tasks; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Drawing +{ + /// + /// Combines two images together by blending the pixels. + /// + public class DrawImageProcessor : IImageProcessor + { + /// + /// Initializes a new instance of the class. + /// + /// The image to blend. + /// The location to draw the blended image. + /// The blending mode to use when drawing the image. + /// The Alpha blending mode to use when drawing the image. + /// The opacity of the image to blend. + public DrawImageProcessor( + Image image, + Point location, + PixelColorBlendingMode colorBlendingMode, + PixelAlphaCompositionMode alphaCompositionMode, + float opacity) + { + this.Image = image; + this.Location = location; + this.ColorBlendingMode = colorBlendingMode; + this.AlphaCompositionMode = alphaCompositionMode; + this.Opacity = opacity; + } + + /// + /// Gets the image to blend. + /// + public Image Image { get; } + + /// + /// Gets the location to draw the blended image. + /// + public Point Location { get; } + + /// + /// Gets the blending mode to use when drawing the image. + /// + public PixelColorBlendingMode ColorBlendingMode { get; } + + /// + /// Gets the Alpha blending mode to use when drawing the image. + /// + public PixelAlphaCompositionMode AlphaCompositionMode { get; } + + /// + /// Gets the opacity of the image to blend. + /// + public float Opacity { get; } + + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixelBg : struct, IPixel + { + var visitor = new ProcessorFactoryVisitor(this); + this.Image.AcceptVisitor(visitor); + return visitor.Result; + } + + private class ProcessorFactoryVisitor : IImageVisitor + where TPixelBg : struct, IPixel + { + private readonly DrawImageProcessor definition; + + public ProcessorFactoryVisitor(DrawImageProcessor definition) + { + this.definition = definition; + } + + public IImageProcessor Result { get; private set; } + + public void Visit(Image image) + where TPixelFg : struct, IPixel + { + this.Result = new DrawImageProcessor( + image, + this.definition.Location, + this.definition.ColorBlendingMode, + this.definition.AlphaCompositionMode, + this.definition.Opacity); + } + } + } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs index d6254c7cfb..0f72a4692f 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs @@ -1,45 +1,45 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Threading.Tasks; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; - -namespace SixLabors.ImageSharp.Processing.Processors.Drawing -{ - /// - /// Defines a processor to fill an with the given - /// using blending defined by the given . - /// - public class FillProcessor : IImageProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// The brush to use for filling. - /// The defining how to blend the brush pixels over the image pixels. - public FillProcessor(IBrush brush, GraphicsOptions options) - { - this.Brush = brush; - this.Options = options; - } - - /// - /// Gets the used for filling the destination image. - /// - public IBrush Brush { get; } - - /// - /// Gets the defining how to blend the brush pixels over the image pixels. - /// - public GraphicsOptions Options { get; } - - /// - public IImageProcessor CreatePixelSpecificProcessor() - where TPixel : struct, IPixel - { - return new FillProcessor(this); - } - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Threading.Tasks; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; + +namespace SixLabors.ImageSharp.Processing.Processors.Drawing +{ + /// + /// Defines a processor to fill an with the given + /// using blending defined by the given . + /// + public class FillProcessor : IImageProcessor + { + /// + /// Initializes a new instance of the class. + /// + /// The brush to use for filling. + /// The defining how to blend the brush pixels over the image pixels. + public FillProcessor(IBrush brush, GraphicsOptions options) + { + this.Brush = brush; + this.Options = options; + } + + /// + /// Gets the used for filling the destination image. + /// + public IBrush Brush { get; } + + /// + /// Gets the defining how to blend the brush pixels over the image pixels. + /// + public GraphicsOptions Options { get; } + + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel + { + return new FillProcessor(this); + } + } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs b/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs index 7f7332a57c..6c140be72e 100644 --- a/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs +++ b/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs @@ -1,163 +1,163 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.Fonts; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Options for influencing the drawing functions. - /// - public struct TextGraphicsOptions - { - private const int DefaultTextDpi = 72; - - /// - /// Represents the default . - /// - public static readonly TextGraphicsOptions Default = new TextGraphicsOptions(true); - - private float? blendPercentage; - - private int? antialiasSubpixelDepth; - - private bool? antialias; - - private bool? applyKerning; - - private float? tabWidth; - - private float? dpiX; - - private float? dpiY; - - private HorizontalAlignment? horizontalAlignment; - - private VerticalAlignment? verticalAlignment; - - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - public TextGraphicsOptions(bool enableAntialiasing) - { - this.applyKerning = true; - this.tabWidth = 4; - this.WrapTextWidth = 0; - this.horizontalAlignment = HorizontalAlignment.Left; - this.verticalAlignment = VerticalAlignment.Top; - - this.antialiasSubpixelDepth = 16; - this.ColorBlendingMode = PixelColorBlendingMode.Normal; - this.AlphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = 1; - this.antialias = enableAntialiasing; - this.dpiX = DefaultTextDpi; - this.dpiY = DefaultTextDpi; - } - - /// - /// Gets or sets a value indicating whether antialiasing should be applied. - /// - public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; } - - /// - /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. - /// - public int AntialiasSubpixelDepth { get => this.antialiasSubpixelDepth ?? 16; set => this.antialiasSubpixelDepth = value; } - - /// - /// Gets or sets a value indicating the blending percentage to apply to the drawing operation - /// - public float BlendPercentage { get => (this.blendPercentage ?? 1).Clamp(0, 1); set => this.blendPercentage = value; } - - // In the future we could expose a PixelBlender directly on here - // or some forms of PixelBlender factory for each pixel type. Will need - // some API thought post V1. - - /// - /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation - /// - public PixelColorBlendingMode ColorBlendingMode { get; set; } - - /// - /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation - /// - public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } - - /// - /// Gets or sets a value indicating whether the text should be drawing with kerning enabled. - /// - public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; } - - /// - /// Gets or sets a value indicating the number of space widths a tab should lock to. - /// - public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; } - - /// - /// Gets or sets a value indicating if greater than zero determine the width at which text should wrap. - /// - public float WrapTextWidth { get; set; } - - /// - /// Gets or sets a value indicating the DPI to render text along the X axis. - /// - public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; } - - /// - /// Gets or sets a value indicating the DPI to render text along the Y axis. - /// - public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => this.dpiY = value; } - - /// - /// Gets or sets a value indicating how to align the text relative to the rendering space. - /// If is greater than zero it will align relative to the space - /// defined by the location and width, if equals zero, and thus - /// wrapping disabled, then the alignment is relative to the drawing location. - /// - public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; } - - /// - /// Gets or sets a value indicating how to align the text relative to the rendering space. - /// - public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; } - - /// - /// Performs an implicit conversion from to . - /// - /// The options. - /// - /// The result of the conversion. - /// - public static implicit operator TextGraphicsOptions(GraphicsOptions options) - { - return new TextGraphicsOptions(options.Antialias) - { - AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, - blendPercentage = options.BlendPercentage, - ColorBlendingMode = options.ColorBlendingMode, - AlphaCompositionMode = options.AlphaCompositionMode - }; - } - - /// - /// Performs an explicit conversion from to . - /// - /// The options. - /// - /// The result of the conversion. - /// - public static explicit operator GraphicsOptions(TextGraphicsOptions options) - { - return new GraphicsOptions(options.Antialias) - { - AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, - ColorBlendingMode = options.ColorBlendingMode, - AlphaCompositionMode = options.AlphaCompositionMode, - BlendPercentage = options.BlendPercentage - }; - } - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.Fonts; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Options for influencing the drawing functions. + /// + public struct TextGraphicsOptions + { + private const int DefaultTextDpi = 72; + + /// + /// Represents the default . + /// + public static readonly TextGraphicsOptions Default = new TextGraphicsOptions(true); + + private float? blendPercentage; + + private int? antialiasSubpixelDepth; + + private bool? antialias; + + private bool? applyKerning; + + private float? tabWidth; + + private float? dpiX; + + private float? dpiY; + + private HorizontalAlignment? horizontalAlignment; + + private VerticalAlignment? verticalAlignment; + + /// + /// Initializes a new instance of the struct. + /// + /// If set to true [enable antialiasing]. + public TextGraphicsOptions(bool enableAntialiasing) + { + this.applyKerning = true; + this.tabWidth = 4; + this.WrapTextWidth = 0; + this.horizontalAlignment = HorizontalAlignment.Left; + this.verticalAlignment = VerticalAlignment.Top; + + this.antialiasSubpixelDepth = 16; + this.ColorBlendingMode = PixelColorBlendingMode.Normal; + this.AlphaCompositionMode = PixelAlphaCompositionMode.SrcOver; + this.blendPercentage = 1; + this.antialias = enableAntialiasing; + this.dpiX = DefaultTextDpi; + this.dpiY = DefaultTextDpi; + } + + /// + /// Gets or sets a value indicating whether antialiasing should be applied. + /// + public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; } + + /// + /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. + /// + public int AntialiasSubpixelDepth { get => this.antialiasSubpixelDepth ?? 16; set => this.antialiasSubpixelDepth = value; } + + /// + /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// + public float BlendPercentage { get => (this.blendPercentage ?? 1).Clamp(0, 1); set => this.blendPercentage = value; } + + // In the future we could expose a PixelBlender directly on here + // or some forms of PixelBlender factory for each pixel type. Will need + // some API thought post V1. + + /// + /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation + /// + public PixelColorBlendingMode ColorBlendingMode { get; set; } + + /// + /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation + /// + public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } + + /// + /// Gets or sets a value indicating whether the text should be drawing with kerning enabled. + /// + public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; } + + /// + /// Gets or sets a value indicating the number of space widths a tab should lock to. + /// + public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; } + + /// + /// Gets or sets a value indicating if greater than zero determine the width at which text should wrap. + /// + public float WrapTextWidth { get; set; } + + /// + /// Gets or sets a value indicating the DPI to render text along the X axis. + /// + public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; } + + /// + /// Gets or sets a value indicating the DPI to render text along the Y axis. + /// + public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => this.dpiY = value; } + + /// + /// Gets or sets a value indicating how to align the text relative to the rendering space. + /// If is greater than zero it will align relative to the space + /// defined by the location and width, if equals zero, and thus + /// wrapping disabled, then the alignment is relative to the drawing location. + /// + public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; } + + /// + /// Gets or sets a value indicating how to align the text relative to the rendering space. + /// + public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; } + + /// + /// Performs an implicit conversion from to . + /// + /// The options. + /// + /// The result of the conversion. + /// + public static implicit operator TextGraphicsOptions(GraphicsOptions options) + { + return new TextGraphicsOptions(options.Antialias) + { + AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, + blendPercentage = options.BlendPercentage, + ColorBlendingMode = options.ColorBlendingMode, + AlphaCompositionMode = options.AlphaCompositionMode + }; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The options. + /// + /// The result of the conversion. + /// + public static explicit operator GraphicsOptions(TextGraphicsOptions options) + { + return new GraphicsOptions(options.Antialias) + { + AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, + ColorBlendingMode = options.ColorBlendingMode, + AlphaCompositionMode = options.AlphaCompositionMode, + BlendPercentage = options.BlendPercentage + }; + } + } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs index 427b240057..b97651dbfa 100644 --- a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs +++ b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; @@ -247,6 +248,91 @@ public static void Convolve4( target = vector; } + /// + /// Computes the sum of vectors in weighted by the kernel weight values. + /// + /// The pixel format. + /// The 1D convolution kernel. + /// The source frame. + /// The target row. + /// The current row. + /// The current column. + /// The minimum working area row. + /// The maximum working area row. + /// The minimum working area column. + /// The maximum working area column. + public static void Convolve1D( + Span kernel, + Buffer2D sourcePixels, + Span targetRow, + int row, + int column, + int minRow, + int maxRow, + int minColumn, + int maxColumn) + where TPixel : struct, IPixel + { + ComplexVector4 vector = default; + int kernelLength = kernel.Length; + int radiusY = kernelLength >> 1; + int sourceOffsetColumnBase = column + minColumn; + ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel); + + for (int i = 0; i < kernelLength; i++) + { + int offsetY = (row + i - radiusY).Clamp(minRow, maxRow); + int offsetX = sourceOffsetColumnBase.Clamp(minColumn, maxColumn); + Span sourceRowSpan = sourcePixels.GetRowSpan(offsetY); + var currentColor = sourceRowSpan[offsetX].ToVector4(); + + vector.Sum(Unsafe.Add(ref baseRef, i) * currentColor); + } + + targetRow[column] = vector; + } + + /// + /// Computes the sum of vectors in weighted by the kernel weight values. + /// + /// The 1D convolution kernel. + /// The source frame. + /// The target row. + /// The current row. + /// The current column. + /// The minimum working area row. + /// The maximum working area row. + /// The minimum working area column. + /// The maximum working area column. + public static void Convolve1D( + Span kernel, + Buffer2D sourceValues, + Span targetRow, + int row, + int column, + int minRow, + int maxRow, + int minColumn, + int maxColumn) + { + ComplexVector4 vector = default; + int kernelLength = kernel.Length; + int radiusX = kernelLength >> 1; + int sourceOffsetColumnBase = column + minColumn; + + int offsetY = row.Clamp(minRow, maxRow); + ref ComplexVector4 sourceRef = ref MemoryMarshal.GetReference(sourceValues.GetRowSpan(offsetY)); + ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel); + + for (int x = 0; x < kernelLength; x++) + { + int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(minColumn, maxColumn); + vector.Sum(Unsafe.Add(ref baseRef, x) * Unsafe.Add(ref sourceRef, offsetX)); + } + + targetRow[column] = vector; + } + [MethodImpl(InliningOptions.ShortMethod)] private static void ConvolveImpl( in DenseMatrix matrix, @@ -281,4 +367,4 @@ private static void ConvolveImpl( } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 4e8284c2cd..0d44db8d87 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -1,164 +1,164 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Collections.Generic; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.Processing; -using SixLabors.Memory; - -namespace SixLabors.ImageSharp -{ - /// - /// Provides configuration code which allows altering default behaviour or extending the library. - /// - public sealed class Configuration - { - /// - /// A lazily initialized configuration default instance. - /// - private static readonly Lazy Lazy = new Lazy(CreateDefaultInstance); - - private int maxDegreeOfParallelism = Environment.ProcessorCount; - - /// - /// Initializes a new instance of the class. - /// - public Configuration() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A collection of configuration modules to register - public Configuration(params IConfigurationModule[] configurationModules) - { - if (configurationModules != null) - { - foreach (IConfigurationModule p in configurationModules) - { - p.Configure(this); - } - } - } - - /// - /// Gets the default instance. - /// - public static Configuration Default { get; } = Lazy.Value; - - /// - /// Gets or sets the maximum number of concurrent tasks enabled in ImageSharp algorithms - /// configured with this instance. - /// Initialized with by default. - /// - public int MaxDegreeOfParallelism - { - get => this.maxDegreeOfParallelism; - set - { - if (value <= 0) - { - throw new ArgumentOutOfRangeException(nameof(this.MaxDegreeOfParallelism)); - } - - this.maxDegreeOfParallelism = value; - } - } - - /// - /// Gets the currently registered s. - /// - public IEnumerable ImageFormats => this.ImageFormatsManager.ImageFormats; - - /// - /// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source. - /// - public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Current; - - /// - /// Gets or sets the that is currently in use. - /// - public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager(); - - /// - /// Gets or sets the that is currently in use. - /// - public MemoryAllocator MemoryAllocator { get; set; } = ArrayPoolMemoryAllocator.CreateDefault(); - - /// - /// Gets the maximum header size of all the formats. - /// - internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize; - - /// - /// Gets or sets the filesystem helper for accessing the local file system. - /// - internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); - - /// - /// Gets or sets the working buffer size hint for image processors. - /// The default value is 1MB. - /// - /// - /// Currently only used by Resize. - /// - internal int WorkingBufferSizeHintInBytes { get; set; } = 1 * 1024 * 1024; - - /// - /// Gets or sets the image operations provider factory. - /// - internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); - - /// - /// Registers a new format provider. - /// - /// The configuration provider to call configure on. - public void Configure(IConfigurationModule configuration) - { - Guard.NotNull(configuration, nameof(configuration)); - configuration.Configure(this); - } - - /// - /// Creates a shallow copy of the . - /// - /// A new configuration instance. - public Configuration Clone() - { - return new Configuration - { - MaxDegreeOfParallelism = this.MaxDegreeOfParallelism, - ImageFormatsManager = this.ImageFormatsManager, - MemoryAllocator = this.MemoryAllocator, - ImageOperationsProvider = this.ImageOperationsProvider, - ReadOrigin = this.ReadOrigin, - FileSystem = this.FileSystem, - WorkingBufferSizeHintInBytes = this.WorkingBufferSizeHintInBytes, - }; - } - - /// - /// Creates the default instance with the following s preregistered: - /// - /// - /// - /// . - /// - /// The default configuration of . - internal static Configuration CreateDefaultInstance() - { - return new Configuration( - new PngConfigurationModule(), - new JpegConfigurationModule(), - new GifConfigurationModule(), - new BmpConfigurationModule()); - } - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Processing; +using SixLabors.Memory; + +namespace SixLabors.ImageSharp +{ + /// + /// Provides configuration code which allows altering default behaviour or extending the library. + /// + public sealed class Configuration + { + /// + /// A lazily initialized configuration default instance. + /// + private static readonly Lazy Lazy = new Lazy(CreateDefaultInstance); + + private int maxDegreeOfParallelism = Environment.ProcessorCount; + + /// + /// Initializes a new instance of the class. + /// + public Configuration() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A collection of configuration modules to register + public Configuration(params IConfigurationModule[] configurationModules) + { + if (configurationModules != null) + { + foreach (IConfigurationModule p in configurationModules) + { + p.Configure(this); + } + } + } + + /// + /// Gets the default instance. + /// + public static Configuration Default { get; } = Lazy.Value; + + /// + /// Gets or sets the maximum number of concurrent tasks enabled in ImageSharp algorithms + /// configured with this instance. + /// Initialized with by default. + /// + public int MaxDegreeOfParallelism + { + get => this.maxDegreeOfParallelism; + set + { + if (value <= 0) + { + throw new ArgumentOutOfRangeException(nameof(this.MaxDegreeOfParallelism)); + } + + this.maxDegreeOfParallelism = value; + } + } + + /// + /// Gets the currently registered s. + /// + public IEnumerable ImageFormats => this.ImageFormatsManager.ImageFormats; + + /// + /// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source. + /// + public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Current; + + /// + /// Gets or sets the that is currently in use. + /// + public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager(); + + /// + /// Gets or sets the that is currently in use. + /// + public MemoryAllocator MemoryAllocator { get; set; } = ArrayPoolMemoryAllocator.CreateDefault(); + + /// + /// Gets the maximum header size of all the formats. + /// + internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize; + + /// + /// Gets or sets the filesystem helper for accessing the local file system. + /// + internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); + + /// + /// Gets or sets the working buffer size hint for image processors. + /// The default value is 1MB. + /// + /// + /// Currently only used by Resize. + /// + internal int WorkingBufferSizeHintInBytes { get; set; } = 1 * 1024 * 1024; + + /// + /// Gets or sets the image operations provider factory. + /// + internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); + + /// + /// Registers a new format provider. + /// + /// The configuration provider to call configure on. + public void Configure(IConfigurationModule configuration) + { + Guard.NotNull(configuration, nameof(configuration)); + configuration.Configure(this); + } + + /// + /// Creates a shallow copy of the . + /// + /// A new configuration instance. + public Configuration Clone() + { + return new Configuration + { + MaxDegreeOfParallelism = this.MaxDegreeOfParallelism, + ImageFormatsManager = this.ImageFormatsManager, + MemoryAllocator = this.MemoryAllocator, + ImageOperationsProvider = this.ImageOperationsProvider, + ReadOrigin = this.ReadOrigin, + FileSystem = this.FileSystem, + WorkingBufferSizeHintInBytes = this.WorkingBufferSizeHintInBytes, + }; + } + + /// + /// Creates the default instance with the following s preregistered: + /// + /// + /// + /// . + /// + /// The default configuration of . + internal static Configuration CreateDefaultInstance() + { + return new Configuration( + new PngConfigurationModule(), + new JpegConfigurationModule(), + new GifConfigurationModule(), + new BmpConfigurationModule()); + } + } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/ImageFormatManager.cs b/src/ImageSharp/Formats/ImageFormatManager.cs index e62805d478..e341557068 100644 --- a/src/ImageSharp/Formats/ImageFormatManager.cs +++ b/src/ImageSharp/Formats/ImageFormatManager.cs @@ -1,201 +1,201 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; - -namespace SixLabors.ImageSharp.Formats -{ - /// - /// Collection of Image Formats to be used in class. - /// - public class ImageFormatManager - { - /// - /// Used for locking against as there is no ConcurrentSet type. - /// - /// - private static readonly object HashLock = new object(); - - /// - /// The list of supported keyed to mime types. - /// - private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary(); - - /// - /// The list of supported keyed to mime types. - /// - private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary(); - - /// - /// The list of supported s. - /// - private readonly HashSet imageFormats = new HashSet(); - - /// - /// The list of supported s. - /// - private ConcurrentBag imageFormatDetectors = new ConcurrentBag(); - - /// - /// Initializes a new instance of the class. - /// - public ImageFormatManager() - { - } - - /// - /// Gets the maximum header size of all the formats. - /// - internal int MaxHeaderSize { get; private set; } - - /// - /// Gets the currently registered s. - /// - public IEnumerable ImageFormats => this.imageFormats; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable FormatDetectors => this.imageFormatDetectors; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageDecoders => this.mimeTypeDecoders; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageEncoders => this.mimeTypeEncoders; - - /// - /// Registers a new format provider. - /// - /// The format to register as a known format. - public void AddImageFormat(IImageFormat format) - { - Guard.NotNull(format, nameof(format)); - Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); - Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); - - lock (HashLock) - { - if (!this.imageFormats.Contains(format)) - { - this.imageFormats.Add(format); - } - } - } - - /// - /// For the specified file extensions type find the e . - /// - /// The extension to discover - /// The if found otherwise null - public IImageFormat FindFormatByFileExtension(string extension) - { - Guard.NotNullOrWhiteSpace(extension, nameof(extension)); - - if (extension[0] == '.') - { - extension = extension.Substring(1); - } - - return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); - } - - /// - /// For the specified mime type find the . - /// - /// The mime-type to discover - /// The if found; otherwise null - public IImageFormat FindFormatByMimeType(string mimeType) - { - return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); - } - - /// - /// Sets a specific image encoder as the encoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The encoder to use, - public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) - { - Guard.NotNull(imageFormat, nameof(imageFormat)); - Guard.NotNull(encoder, nameof(encoder)); - this.AddImageFormat(imageFormat); - this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); - } - - /// - /// Sets a specific image decoder as the decoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The decoder to use, - public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) - { - Guard.NotNull(imageFormat, nameof(imageFormat)); - Guard.NotNull(decoder, nameof(decoder)); - this.AddImageFormat(imageFormat); - this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); - } - - /// - /// Removes all the registered image format detectors. - /// - public void ClearImageFormatDetectors() - { - this.imageFormatDetectors = new ConcurrentBag(); - } - - /// - /// Adds a new detector for detecting mime types. - /// - /// The detector to add - public void AddImageFormatDetector(IImageFormatDetector detector) - { - Guard.NotNull(detector, nameof(detector)); - this.imageFormatDetectors.Add(detector); - this.SetMaxHeaderSize(); - } - - /// - /// For the specified mime type find the decoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageDecoder FindDecoder(IImageFormat format) - { - Guard.NotNull(format, nameof(format)); - - return this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder) - ? decoder - : null; - } - - /// - /// For the specified mime type find the encoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageEncoder FindEncoder(IImageFormat format) - { - Guard.NotNull(format, nameof(format)); - - return this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder) - ? encoder - : null; - } - - /// - /// Sets the max header size. - /// - private void SetMaxHeaderSize() - { - this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); - } - } -} +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Collection of Image Formats to be used in class. + /// + public class ImageFormatManager + { + /// + /// Used for locking against as there is no ConcurrentSet type. + /// + /// + private static readonly object HashLock = new object(); + + /// + /// The list of supported keyed to mime types. + /// + private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary(); + + /// + /// The list of supported keyed to mime types. + /// + private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary(); + + /// + /// The list of supported s. + /// + private readonly HashSet imageFormats = new HashSet(); + + /// + /// The list of supported s. + /// + private ConcurrentBag imageFormatDetectors = new ConcurrentBag(); + + /// + /// Initializes a new instance of the class. + /// + public ImageFormatManager() + { + } + + /// + /// Gets the maximum header size of all the formats. + /// + internal int MaxHeaderSize { get; private set; } + + /// + /// Gets the currently registered s. + /// + public IEnumerable ImageFormats => this.imageFormats; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable FormatDetectors => this.imageFormatDetectors; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageDecoders => this.mimeTypeDecoders; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageEncoders => this.mimeTypeEncoders; + + /// + /// Registers a new format provider. + /// + /// The format to register as a known format. + public void AddImageFormat(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); + Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); + + lock (HashLock) + { + if (!this.imageFormats.Contains(format)) + { + this.imageFormats.Add(format); + } + } + } + + /// + /// For the specified file extensions type find the e . + /// + /// The extension to discover + /// The if found otherwise null + public IImageFormat FindFormatByFileExtension(string extension) + { + Guard.NotNullOrWhiteSpace(extension, nameof(extension)); + + if (extension[0] == '.') + { + extension = extension.Substring(1); + } + + return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); + } + + /// + /// For the specified mime type find the . + /// + /// The mime-type to discover + /// The if found; otherwise null + public IImageFormat FindFormatByMimeType(string mimeType) + { + return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); + } + + /// + /// Sets a specific image encoder as the encoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The encoder to use, + public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) + { + Guard.NotNull(imageFormat, nameof(imageFormat)); + Guard.NotNull(encoder, nameof(encoder)); + this.AddImageFormat(imageFormat); + this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); + } + + /// + /// Sets a specific image decoder as the decoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The decoder to use, + public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) + { + Guard.NotNull(imageFormat, nameof(imageFormat)); + Guard.NotNull(decoder, nameof(decoder)); + this.AddImageFormat(imageFormat); + this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); + } + + /// + /// Removes all the registered image format detectors. + /// + public void ClearImageFormatDetectors() + { + this.imageFormatDetectors = new ConcurrentBag(); + } + + /// + /// Adds a new detector for detecting mime types. + /// + /// The detector to add + public void AddImageFormatDetector(IImageFormatDetector detector) + { + Guard.NotNull(detector, nameof(detector)); + this.imageFormatDetectors.Add(detector); + this.SetMaxHeaderSize(); + } + + /// + /// For the specified mime type find the decoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageDecoder FindDecoder(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + + return this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder) + ? decoder + : null; + } + + /// + /// For the specified mime type find the encoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageEncoder FindEncoder(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + + return this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder) + ? encoder + : null; + } + + /// + /// Sets the max header size. + /// + private void SetMaxHeaderSize() + { + this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/5116.DCT_Filter.pdf b/src/ImageSharp/Formats/Jpeg/5116.DCT_Filter.pdf index 3423fb315978c281521c14474b8b401b65dfec81..a5967a02e4da31ff6dd505a25f5147d53d8d3375 100644 GIT binary patch delta 387 zcmV-}0et?_stu5;4GtwxL`E$!E;9`!;^WWLu@4~#voQ!59kWU$-2$^HC{zNoeJXnb zv-vDn0<%#sUIMeQF%bi^!ZdCIvoSZ}0;3Z;3$qkOC<3!RM|%Xbxl1e(v*uk)39~b2 zBm}cqYy1eaRdgo{vweRN3A2xg6biGXlI9Ati=D^|vs9^M3A4GfWeBq$yJQEmgu*lm zv$@Ra3bUivc?z=~;zb9uW9dx^v$6703$q0NND8-22mz`JxA+nPjR?1f8vz3ew~8bI znhCchE&(+Ow_7&>2n)AlLIH6Jx7JGmVhFc6Q~?|bw^m&N!Une)WC556x3X*jSO~WZ zbpeY3x0iMSKM1$iegS<3w^fA!X9l;uiva}%x5JPDvjn$yl>u)8x967uxdpeOodF*O zx51(TT?Mxvr~#J+x3{eU$OgB7v;m+4w;{Lzy9c-3zX2EqxAw&WfCaaA%K;e*w~^HW hhXuD8+W}Mpw{G15p$4}Q-0( delta 516 zcmWmBK}eHf7zc3v+U8gBl}ZQ2B&3ZES(}n$Fc?9(CYcZ^=w%+Zm87ExER>=1VhdKZ z7O@bNW{2h?p-p<3I|M>7P!N2JE^U@)&1wH@MkLP**&-e1aUm~mTWc9P=TfNsW zb=xoN=IY`0uKwvvhXy_G&|2CBpLwrNFjD(PI7>l6>VSJ;9k)^F85-U#&xvG~V@6iDB$v?Nb{o`MsjFe47mVR%>bNP%4 zwxM29>aGb3;%7UYES)ZBvZXv%@sRauKLU(54&gj&n;#oS@plk!*nbkn2;HAV9OAvl zxWYO!iy7w3^BAMg;xHJ*mkH$M_Nw@!+d-KL5&+C8sh@uzJX(O;s$7p`A)r8)pft(ukNqUz4zR6&pqcm=RO{P zVb3d{-m^5}&`JC4XzgSdC%pUKnwPFCKircu<9}gbjj8`M=8Ws!>4_59_AR#oH=w|= z+aS+tKnpCRIoh{8zkw&CL(d7@Aiufvf{UA@Eh}_rZOe14NFNQzZK|{2#evbGYdRzv zg*HzZMNU)01!FrJn%j=+1^Q?}p-GT$Mg#kvJuo`5cx}^+Y~Hu+1WgTVD?7F}Y9Dlt z?7lm{f3bEz-^^_7zsEjZsP*quE7rE(EiIp9t zdTPaA{n)4-d%mBlU0*rTsBOCF_-u9M`<=bDS@WkEwG~&T^VPu*rh04ZZk%e=cKq#m zx$4C#-CKKT34SdtU6>c)p4y7LCL6WP{~lkcUVKufr*^@^`1OfLzJ%~encmv!XZAB{ zy{k@0{yOi~$j#PijINngNrH$#u`WDqb zJpwH4HEthWz3#PAk2=vYW*KUYYaBQv26mckOf%Fa`y03ZAO`k0StRZ_#n^q2+SqUW z7Jod?IQ(1n+SwfyLmhc;$5TCO%>0g%4K??wj`c&;%$qw-G1PNQI!-IA@NU5u)jFnU z)u!bgM;mJDBOUAVYR6|gt}@gEt2%x)sCx63jvh66ea8$#{qc>CEu$2GIfm-}w&STW zYW=8AH?0Pb?Zi^ojq7}Lf;wz}`T4~II^W1w@pF%Q?C{PL3^jgw=U$h@zZgW!)KC3OBKO(;so91)VM?l_Cmz^;tPFhN zxK#h(7&zuE0q&Wdx*?G`_VUz)hI;*q)S9j9o~0hWJ$06$%sW$W7ZqHs-%#@&O8sb1 z{Kq#|rA{)`UaM2X3o&r|T2b{+>r#i8;stivC=1N}I5n+M?YJ`CqdxswfXSOvj}M9^ zMrYFi`g7@2IR>s7A;5-_>5~#AXY7zZ3P-bJ`X7EQv1(@eAVc+?oW34D3x8KD=cKXh zh_llhdSYPMl>(eGFa5&s7?^gm0Cy}&zuc=Z1Sjy${b{(M+z)lold|G1Po=ZTijTb} zz^HZUpO1(o{^JV)KL0YkUtcn$*d;?sU56%y%Z}@si50i*dU1%_)9-q=E0!C*dlyta zZI7-ShsVIsBV^zi)4TQ>TK%6{UGK-}t)~lWoZ0owuox&`A;76~ySkGFFI^Nk|@6900F+8lDWK3A&B)GYRk;b`ugZz_3`-`ob}!pX8JnSF&AZS8L5n$GN&2p z^xtQe*B9(nd)zA`H{O>i!(k8;)dSDSghN(kMkTh|>wN)!_d#Z2BJuKO0e1Q(b3tO7 zN77jU!@IK!e68ru^fQ3u5lM>+T`NGr1K7g&{E3>S4LXg?jW? zmu#JbvDS^x{aZE$&fh7AYyZ~HxlCGZaC5K5Swj6K61g3A&8^E+XCImCRih>e@QZzO z-wja!FzXvfQafpQ+e;KBL1!(!mS zCg!n)!Q1Aiq}0Fen4dIM-Mn|cAJ=D6{-L2&{M@TH9+gL2_KwM4lK^Yal1Z#N`GXUy z{bpVsc71Dp{*p|r;5iEfcfqhHSr`0GDlFydjxm$54PP zqGAPJC=w&~5n#@wV$`edJhC`-Pz(?GSrJpu_<3=8BDeF|0$gxTakpfx=Ps3j6D}{l z-mQ#(FUk(CEzaxAg@L;3%S!rb=v2uX`r5j4u z4vOj7-M0wv{jyRf0Y)hSPQAZ$Kmz>fX#s{jTl#fLf%737|7~q)TuFxasvmAB!P(B< zSTYh|{w4ul*<9LE2YS?d-DM0nv*laL@roP!1n@_e?@A=L{IHBOnz~*2<6NWKes$9;h~jzGUT=STbZ z*+zBchvogN!F%P+^`F!)-!G>V@W~I#-w1ew>iej?s8opyJv*|(W?j~R94_eW$n{Ce zv22&BG{W0Cq^;JOS&6*C}Pdy&n);zc-M{X|g9^OC-0@sd8gla};tKWbUAZhzknU7p{L z0`hC$^}IGHFv-@w8#)a6bypXIHcajNp;JF_tUrK70cjDKu1_Ztn7&OO6_^2gPZ0Wi zA%ZaEn;L{sC89n@=mhNQ*buuq+BNLzk=14k1Qnkw7TKoNx}w-0C+X3aBHuM>IzAji_Dw|tys>;#pD`GNVSZhk-kMH(xxSusG~Ku4^8AprSw?QydL zx`Tj1zoPpJu!v5?3J`Tku>dA(abnmIQh}qpIwE>V-j5y~S*(KNa4f&|NHAF#aG(d| z)9nHjcr-9VydzyB&q{3FG^VUW75yk z!HsLWXY8GTIIK9fZ4pgyY|o+C4+zmmw}srw0jR!z2T{=?!#W75q?ls@)F;DhhOIjp zNV7x10gz|u;o5N`v;MLo>o?4gG)u%8VvbE`iEu$w-SHfxOITxdpCMDa&{ z?Yb5dL)>tbRQi&*G<1}Z3iYF`fZ-!|krt9uNed8LN;kM6$f5d@C^RT)xjs^A>xtt; zz#AG+gMR@-rqw44oR9*&iwI6{#DF;U0z%l*=K>dHCjb|9J)M8yhFk0e(1ETc(huix zKsH@VFi`{+T?=Fpy3ZAeac%E`WfIu}C}=X1gnyy( z2G7xy1qAnJC=3a*(TLD@DO8krKOmP}(Q`}*{StqhArP2D?gPkc_(~Eeky`M&t}BMO zak`e0faqGHZ^Kc=tGLqP(ID9fFNAKh+Hv(b@WK%MV1#bBIYBkxgI-Z{lxshzzsDm5costa{!7W-4)R{fHCxenBLbj z5CXVEaUM{G%TiR?Xq(VvNE)OVihGu_J$4oFHtx;>0+AY+UiJ^}&g}$10l5#Lh}_3_ zLR6Onlhx-6xmO1%H`MP4s+}Z_WBrEtVF^mw5R0fa;iD_cw#L5cruyL9)U7~}w~4F( zaw=`}2h7z`hlS+VzI2}G#N~V$a@$VVN4jr4O6(uaC@$pDc;a*h2;@q)H7GJAJwGtH z6${9r-~gzt|AiF;y3l}_UZWufwxVl+LXVgaSP0yQr5v64NR$NsCNW$$Dg_|M8AA9l z^`HU84egfuEaf=q8u?VhRZJhXH!&YT{GI^2a_|Af!3PldwYG7Dev7g(AY|28s=>b` zL$uaK*hYW`Rg)*gBpeV9K%ZY$B*=DAN$OGv4s;^$Fxw1t{)M6lu}mL{7anqTNI7j% z=s)s6JdltU4)TaDRS8^_y*h?>0-TDj1t>S&anybRks(0*0uu-)py@#Xn!GvTUpPqC z43I<23MDl{lCBt9Zq$Q@6uR8H6rwtLKblw}JYC{%GXw&QcuN5S4J{d%T!{s^fHnKl0}u*ReKNc-5KTZ| zk(>K~__-xS0ZJJnv@7Vt=_W&B=jtZ39f!;V2y|Lk%ytl-2|_!8)VJ|n?ZD-#?;il) zUK}NEp+1>|_OH%NAjSQ1Y>|77fa<-*))PVKuhV{6zhP@oX;u~o(xR+PfG}xNxDN4H zL5v#^H&g-f15)Tl6;vtuE)heBCxDi4g9n5Ftu1{Z!YQ%L(6iAfASrO-misLG$2$Z_ z4T*t~5rhauHx-R)jx=agbL#`I#%LMp)BE62^*n|38|KFY7?rponspTK0HJWC$#7K( z!eb+)@i2%?n`{B5TOoQKf;q7H(HSCmH)kIf9F+ZwUoY zn_}on(^ZPxHouEN`N$R#8iqQAhGFAzzuEr+hC|V`0K``b#K)%+7ZCT$0r5Kx`g!P( z=+j&123GWqLOOxSA zlC=P5(kH`b8Yog|Anv6eC?f|XwlP=2zku=67A_a}d;oFJ2cd_fE}qL_Ze0r({JODZ zOr;|iEfea0q1xfLJs^j&Js_8u71|5j+y{j3b#yJj8;C_k6~v>4FD8LFLcfCVIC>M5 zZcw#2eBvl%D1t-E=bMU0nE`$j^DlW}pywh$+<-yD3iXq|CDa0}KK{cI(Hu0h;Zrma z4KTzD$$!nOinb`leq51|!%B$fWu2Lz1C7S3K0lH+??}=yok&>02Tr6}2r|lI5geQ5 zhk+>G!TqUyCoO1p0=Rn1^!5FNec{_8eSX-iS)?Q$i)NQj`9O5>o}g(V5FDs=EXG4>D4L*JX0^d0>ULdr+$jHU1)7eV-bM~j`}t<(V! z2suIqV*Q5s5u9~`0Wl%d`uqsjE$vgx04aHnVku%KB84w(SSqOK-Y;VG!6hJ&UCsLi zLfoavJYR28id3Lq2!Zt1B7A=&nEnI+2wgJrYrHS(=|-fS_JQ;a;5G@SyVFxj=ZLZMN`4_YZLOl`)1cF3bgm`>n2%q{e!NTC!PpYXGFdbc*)oMYnb1mZ)XL<^M`I~ygvqQe&>Ky>SFLGo#tRN zw5%vz;L8)8_luOvsTZMPpcJU{FA)RL;U%dQfE3=D*s!1+v?W|hy=a$#UQ0vu>^Ud= YY}QZb6nERLxbuSNJG!cGO&m1ypFr_29*&*&}w>OG}~nHF;+Z0`Qn zO7w$gnb8l`0Zh+vDCQcLrD9GzX!Tj&v3x4$J1$kIVi>+bL130m9yT1yU@B%)r48Rq ztWYXr__j`!HkfZ>j=`Lth#rG6i>?Hi6Hh$9Pea6+Y1zI`R5D$iQO-;k3!-w&P*l=& zW>Pubw7i7sH&2~--Q*zX*P+<{TtkOcs@VQYWA_$(?9PoS;$vX9tF!ocpwET^KE@4d z=V0#COecJFI)5$y_UFq+=kW2JYjgbDfSaCbP8H|RJ2mv7_g=)M9=fk7FQL}<-#*Nt-#v0W#^pk-?e{McLhnAdkPBUK zcXK&-!PWZAUsP#rf9dH?e1NV0cLh`oma}YY`+2L+<aF(4K1%f5`!8s|eOz4kD{ddnA_~kWH^72Wk(>mbC1=CWGa3TBl z)Yz}_Ip^kv3jdzDwPAfLJ~z#2;5vrReGPAS#wRv|uPMB3NyGIT2IZR*9$MZYW*XVv zFph7J0NYnJaN@$RRyXY6+zW8uD-E}C@bW)3+=1zX<-HqQ!%zOw!1)YE?QA&GqXv#U zBEgkkHLUNAAE)@{opSi(ehD7@qVdBB+<&Zb93)qaNYB9rgfOY= z`Gcow=_8zHZernxzUfOjxTs%xI@hr0fb`1h0IlI(JADoRag>`rK6>Jg5$QG#{&i%! z0VIoIR*sjWTsk2=tz&KIT{lQ@NL%`$nqau=r|IeZ>C5j)Z|YZ*9Qvdj@^??A--D)7 zv!&0aujZ23=hJ6!wuQQv{YiprUQ2i77A?S!w#kWx?nsX<)xc#RN-*_t`l3j3(jf`1 zIh=km8uOt{lc39QR9wACf}NK%y;ZJ(M^`reD_{1=XPfx3kt=L} zNlNC|H)SJ!`)`wAWqZ?q99NV4<_ig4e4y!^ZZ+_?g=VqBrc(2mNTnb4X%_sx*0*^x zHaGmW)BGyVt(@?VQ=5erFQ3-@FxQv+p76%8&D@^C^_Mgc#c>KUlbiQce_Qz@`O~;5 z&0lqa2;>EatL|xT;cAHIw=R@IgBCSU6GsJ3D}45OsauED&7a^%2)7k#yCpbjPxIcs zHSmg-7O{$FGc7audbxuONB5QB(ta)TxlRHc#U%LHkd`6PMuMM@X%S00^}?3ZxrGbK zpN*H2A53T&8%d77rRDp4osZwzV)m~|{`J>Va@eCSH%5{#uaID`uq7LTLtdBQyp1hW zBe3fS61-t=%UDi`n0@Op0j?OF=@AKz?~>uGd}2k{O!u1H218(BZ!5DTTI5?JBzW?9 znY*JAU%E0QHg&+&nYW`MyZu;7-gsxGTO-yryOLQNjeX`X<&Tg2DzhpYX3{efT(csh zaf1=szP~|&?A1(;UfJ#6;73X2l}j@0I;c_3?7}i<2aHbYS)w?#&=6EbRDGvS)D%#rJYJ^}?*M&{r=&4bN5b%` zx3a>Pp5BtRL9@UPeIn(~|17(wCUbN<-MZAG*W@uOJ#_lD$lWUA92H!LUUZ5y6DisLL)nlVmd z+b_)>hTRBk_Y{epa$_#VcX38LR2nl!&i=%`xq;jh32^U137)nnHym3V0u-};@=UHL zwk6E2&GFm^qn5+|ugg&@8*|+`*T2}5(@(7aw)3DI$3C2!Ac-l5kC*a-gQHr0mL#wo zK5}AStoG~v`SbWH&;DM1cungCL-U--mBaGS*PxA;=EWX#|M&a@HEh)lc|k(CEk7Dl zg%fAzMQ~m_CqENJ1^8YLFPWDg#dTafKmRaKlms|xp#&d(G=JrHs#N9I$kDD^oBt9U zCFr~Eb&0Lpm>-OX1=hG-Vwdg6F94+i%N@vz(0${<{IB3c1lFr(L0H4gUWM5(FM;*+ z3PR&s{K6EFDzHPtBzFGUg?Zdh_PM)I4i8*W5R8qwvakg%F$5@fya=35+VMJiJ94m+|TKA8_7zF09 zW2q=~zEdk+#BC4X%i-?t7RADboK$>>9|sA#RnRY1a2&*FuMI5_YCgnR1Lb?kfHT`M$vZaG%7w{H*vIw?)31 zgO5pU^wQ$48g|wyiT!eQF@PTljcsx@3$_=JKqkQVa=7gCqM-M{{^CMzTmpRj8wu_| zS{#O11Ej{~a70r{NX~CAea7?U8O4&Y)%QxJojeW;q0OD8(D7YMmqp-xJterMS7}mh z5o1m)2?7@MFCDJY|M(z@eKEN7P7T}bN^G!SI~M*t&nc~! zCea$cGe%-3U0BK@Q4(_JOqAHXD@$)iB)3WIk5fzM*W^x^DY3S@O6zKJXU~(^-1()S zV8{EG=btc82feaKQ?>*nKNYBO`G5dI>(Tq4b{^O_mJ3x60urZ!7hUz{Ptd z`02hG}sa!Z-y9jm@0-VPE(GPFYCi5&? z47YEV9PW%c+N<1i3Gn!Nnn+A;nXhe$CZ4cZs`%s*Z9jHTO8)N?QnF>4_CNekiN{}F zA;Is2+N$XB&;F?O;uB1NLu(7Bzpgp>yWeK*`k?1V@%M|HwOfM?^6x2s)@}$VZ_+wK zUH-u1Qg#tOAodj20EsM8j9}VZ+N<~&xC3 zuARsqdhl)SEsSy9PVHVk!I$r7Tk!RjcQsLO40&HWlhbs^E^Rdb8Q(_)+8*tz>fhS2 zyHbsfKfU(q;rqUv`dkA_j1RO&OP#BY8Iz%-5j78jjt8{3|G;cF{z%=mW zO`n>aW70l_Wx5{iKUk)3(H=*|GLtQl>VS@|^lU80@#EG>P0myLIF|1kwC!T~p3=Rt ze1>KX>DkzZ>v$1CauOR&rI?vu=)ssY+h&aRn{3Ch>HM~1x{1b%XrtM_qbpPSuCfU> zbf@dCIM9=V4)mnE@Us;<+9@ia2w=ORNQ$vNMJUM@$v; zz#3hM$2^OeJ)FDGk3v0Z@VN?UzGb+vNd&2{V$yuirAN$w3vyy{$vcDZqk1yOPzYxZ zqE7wMHg!i=u!`=O)Nw-4=1!XbSL>^rhJz5KU?wBh2RYj&N}GmyYXA&RjgSV=rzo?(NeX>EoEb4-Q+4N|ej_=Uu3CyGS31-ECo^;`^!Dz%VUDKe} z0t_*l5Q$Sq{hWzVqsqZMQ%f^FTtm>U_HbPh?^ZDJo0v4%l(M;=!xY|OkUAaK*Og$0 z&0!w#4kqq{OuEL3@9G}4Dj#f6Yw=x6QHJS^`YPDBuIxTD48Ok4B_fx~tPGE?{tBk+?XX7Pz3t6Bu(y8e+ktDsqOcWCK>kz-fwgl9M3Qp&O1= zz^9i{z!<%(vkYt(*;Nb2j#?=&;b-c6nq`^>%^@t)v66vc-LaT7emaS-vfvdBN`|Ey zG*5&aP(rR{no{(l9M`AQmo^la|&e)uC2K zVQ+C4Ovc52Q`dqT3SOGZxt>BALmo?rv@r!8&#vgE?vvfJbyz7;#@2n*NA(liaA{N- zU`oKS4TL#j_P}gz;dP_|MBFBm;KU}vVQ@i1M4F9IO*h=e>jE?<0|tAc!+JhFil`lt zU=TSRS)Fo$<7v^OXxngflt`3?)WjjDB#k)iD7eVPI7D<;A?Nd5ttZVzNTV9^C>tvi z%xH{B!>EaqD^3K?jq;X;?IOt14M!sJEf``i(cQCr>htXid^vqX!}gdGj)B2}kpYpQ zCX7HTjr^24XB>7zu?VQ598ZKKCB6eQlgD#;IwlRFgq~)ku}aPFAn&C47ciIRU%)&X zc^$*Bd6ZA)tPb8xacPu?phxo+l;%i)$ion$VHWL)J{>Z1j+bPeZ>4Z6G37f$lWQJ2nCfF;ien z8PL&nMb2?`>H{29bjl7ou6XUI?s#JJ88uyG6-L5zgN`jRX`b#XR^aKrVg#t96(fKR zP`?d3Q~WzHlSXK#;+r0=6&)Nq#hiIrNZbu74CX5sPIl~qq*eKtgkm^|3>H~W8UsJX z`8dpQ{1^*{W@6HqgANF>0>pTAE-*y(gk4a#3El z&5JrqbJ0vy7zYW7BO5Hv#ia4PmyU}*W}+z~$v|SF$l)YXqeeCdyCQn>-KAIsE23Qq zw5eQLQvy?}eYB~PflVrieB7W!B7IH-Kq=?xYUkGVkeN~z(1c7d9&FCXq=8{&;hu%G zg)AHuAWeginZSe;Ig2K@aGG%3L=H_z^t%ZNS4txW&y{J*8ko`oMAXNoG^r_EPj zXpEEb;NX>t0DUi?nmsU(a+3H?T-lnSO?2l)J5b!sg>N>my#fH4|T5%86&$2U>R5`uss zu_nrx7H&Jp@#L?$QxgY<|D%V4bsb~U&~c*mJ@z)~0>x1TCR*#sQV0uF8k4}3#w3pe z)W7=`v;Y)6u`vlf<-|68X{>t{lik-kWo#^jlqWu}Uufp;!>91hN1aath&U1vBqxDW zNiY-a?-Y{;mr4^)-!aiephF--CT;|`7D#WX9MUw3!Bf#A>sL_CP=ABYw$dWO-7~t2 zWD*yN6*XsEq0vGZS6fJ0>9Y()B_++pp`}`*mQDD%piem_4QVfR+&(f1g?F@0sLOyH zqmdW4CBo0v83As364T3ltoV*-JJ$>1)`9L8N+DH2bg`*lV7BP}*Nq2PrC1+BYmt(M z7ZsFu1%P31BF2LYI!B}-3vy}g3ruOP15++`(Dg(2BpOzbfh|zh4GfpNM6QDVDQ#op zt{5#sB8Xd4YR2YTj6FU>@BpEjm#)$udlT)tdfT=sk>w3C_DAlM7 zK)i=dP}2By3uPREC9%3-dDoaU3&jW36EP=Ym~uT9awxhNxO<={harT~Tojl`lVDbX zO0*}26Q;C=fGMpZI3UFlcP$wuW!9N>JS9W90p|)yug&m zD}tdtV$#46$^X!%z}-Xr{2XJXR0M{^vz|0yxIwHx#vDU7TDL|+ujn|kR1mGMM6;9= zglm0%y-FT7EL)XBDqFu>c$Sw5^jYi{AD4i@)}NwaVl8~-FX7i|ENNMi^TB-EPh4CP5Oug>_UO{`f7hHyp)d*Fl%WIi1VK?6 z@j}@IWFgA&w2<;DlQ1+hqKSi4?*wV+f6(KJs~N@8c>PHA#J}85oO($RF!K79ffA0^ zx3DyTB%kDh$PpqvYXdTcG+d`ECXRb^lKlfPXej5H=-by@8bHPQp=*&a`@rZElZG@+ zaRmrh3X#AROH(hpxlSyxKh<>`OxZ^i>~O!7AQZyX`uGbq)PLAWzo}Ir_f{;;HeHgZ zac0H8`X{6zg;pj(REl>nnAkT#nw5MVx4KABT%QwSE}T1Rz-VA9kry^aG9EmOwie~5 zf>&77ox!{2R1ltmR0(`QWI&W~n^p;s!wr=}F0v3(MsYz@sdPZ3rZJJh+ - /// Options for influencing the drawing functions. - /// - public struct GraphicsOptions - { - /// - /// Represents the default . - /// - public static readonly GraphicsOptions Default = new GraphicsOptions(true); - - private float? blendPercentage; - - private int? antialiasSubpixelDepth; - - private bool? antialias; - - private PixelColorBlendingMode colorBlendingMode; - - private PixelAlphaCompositionMode alphaCompositionMode; - - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - public GraphicsOptions(bool enableAntialiasing) - { - this.colorBlendingMode = PixelColorBlendingMode.Normal; - this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = 1; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; - } - - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - /// blending percentage to apply to the drawing operation - public GraphicsOptions(bool enableAntialiasing, float opacity) - { - Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - - this.colorBlendingMode = PixelColorBlendingMode.Normal; - this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = opacity; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; - } - - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - /// blending percentage to apply to the drawing operation - /// color blending mode to apply to the drawing operation - public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity) - { - Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - - this.colorBlendingMode = blending; - this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = opacity; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; - } - - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - /// blending percentage to apply to the drawing operation - /// color blending mode to apply to the drawing operation - /// alpha composition mode to apply to the drawing operation - public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, float opacity) - { - Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - - this.colorBlendingMode = blending; - this.alphaCompositionMode = composition; - this.blendPercentage = opacity; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; - } - - /// - /// Gets or sets a value indicating whether antialiasing should be applied. - /// - public bool Antialias - { - get => this.antialias ?? true; - set => this.antialias = value; - } - - /// - /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. - /// - public int AntialiasSubpixelDepth - { - get => this.antialiasSubpixelDepth ?? 16; - set => this.antialiasSubpixelDepth = value; - } - - /// - /// Gets or sets a value indicating the blending percentage to apply to the drawing operation - /// - public float BlendPercentage - { - get => (this.blendPercentage ?? 1).Clamp(0, 1); - set => this.blendPercentage = value; - } - - // In the future we could expose a PixelBlender directly on here - // or some forms of PixelBlender factory for each pixel type. Will need - // some API thought post V1. - - /// - /// Gets or sets a value indicating the color blending mode to apply to the drawing operation - /// - public PixelColorBlendingMode ColorBlendingMode - { - get => this.colorBlendingMode; - set => this.colorBlendingMode = value; - } - - /// - /// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation - /// - public PixelAlphaCompositionMode AlphaCompositionMode - { - get => this.alphaCompositionMode; - set => this.alphaCompositionMode = value; - } - - /// - /// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings. - /// - /// the color - /// true if the color can be considered opaque - /// - /// Blending and composition is an expensive operation, in some cases, like - /// filling with a solid color, the blending can be avoided by a plain color replacement. - /// This method can be useful for such processors to select the fast path. - /// - internal bool IsOpaqueColorWithoutBlending(Color color) - { - if (this.ColorBlendingMode != PixelColorBlendingMode.Normal) - { - return false; - } - - if (this.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver && - this.AlphaCompositionMode != PixelAlphaCompositionMode.Src) - { - return false; - } - - if (this.BlendPercentage != 1f) - { - return false; - } - - if (color.ToVector4().W != 1f) - { - return false; - } - - return true; - } - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Options for influencing the drawing functions. + /// + public struct GraphicsOptions + { + /// + /// Represents the default . + /// + public static readonly GraphicsOptions Default = new GraphicsOptions(true); + + private float? blendPercentage; + + private int? antialiasSubpixelDepth; + + private bool? antialias; + + private PixelColorBlendingMode colorBlendingMode; + + private PixelAlphaCompositionMode alphaCompositionMode; + + /// + /// Initializes a new instance of the struct. + /// + /// If set to true [enable antialiasing]. + public GraphicsOptions(bool enableAntialiasing) + { + this.colorBlendingMode = PixelColorBlendingMode.Normal; + this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; + this.blendPercentage = 1; + this.antialiasSubpixelDepth = 16; + this.antialias = enableAntialiasing; + } + + /// + /// Initializes a new instance of the struct. + /// + /// If set to true [enable antialiasing]. + /// blending percentage to apply to the drawing operation + public GraphicsOptions(bool enableAntialiasing, float opacity) + { + Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); + + this.colorBlendingMode = PixelColorBlendingMode.Normal; + this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; + this.blendPercentage = opacity; + this.antialiasSubpixelDepth = 16; + this.antialias = enableAntialiasing; + } + + /// + /// Initializes a new instance of the struct. + /// + /// If set to true [enable antialiasing]. + /// blending percentage to apply to the drawing operation + /// color blending mode to apply to the drawing operation + public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity) + { + Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); + + this.colorBlendingMode = blending; + this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; + this.blendPercentage = opacity; + this.antialiasSubpixelDepth = 16; + this.antialias = enableAntialiasing; + } + + /// + /// Initializes a new instance of the struct. + /// + /// If set to true [enable antialiasing]. + /// blending percentage to apply to the drawing operation + /// color blending mode to apply to the drawing operation + /// alpha composition mode to apply to the drawing operation + public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, float opacity) + { + Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); + + this.colorBlendingMode = blending; + this.alphaCompositionMode = composition; + this.blendPercentage = opacity; + this.antialiasSubpixelDepth = 16; + this.antialias = enableAntialiasing; + } + + /// + /// Gets or sets a value indicating whether antialiasing should be applied. + /// + public bool Antialias + { + get => this.antialias ?? true; + set => this.antialias = value; + } + + /// + /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. + /// + public int AntialiasSubpixelDepth + { + get => this.antialiasSubpixelDepth ?? 16; + set => this.antialiasSubpixelDepth = value; + } + + /// + /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// + public float BlendPercentage + { + get => (this.blendPercentage ?? 1).Clamp(0, 1); + set => this.blendPercentage = value; + } + + // In the future we could expose a PixelBlender directly on here + // or some forms of PixelBlender factory for each pixel type. Will need + // some API thought post V1. + + /// + /// Gets or sets a value indicating the color blending mode to apply to the drawing operation + /// + public PixelColorBlendingMode ColorBlendingMode + { + get => this.colorBlendingMode; + set => this.colorBlendingMode = value; + } + + /// + /// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation + /// + public PixelAlphaCompositionMode AlphaCompositionMode + { + get => this.alphaCompositionMode; + set => this.alphaCompositionMode = value; + } + + /// + /// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings. + /// + /// the color + /// true if the color can be considered opaque + /// + /// Blending and composition is an expensive operation, in some cases, like + /// filling with a solid color, the blending can be avoided by a plain color replacement. + /// This method can be useful for such processors to select the fast path. + /// + internal bool IsOpaqueColorWithoutBlending(Color color) + { + if (this.ColorBlendingMode != PixelColorBlendingMode.Normal) + { + return false; + } + + if (this.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver && + this.AlphaCompositionMode != PixelAlphaCompositionMode.Src) + { + return false; + } + + if (this.BlendPercentage != 1f) + { + return false; + } + + if (color.ToVector4().W != 1f) + { + return false; + } + + return true; + } + } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs b/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs index 2758a74808..0d182d3c24 100644 --- a/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs +++ b/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs @@ -7,12 +7,12 @@ namespace SixLabors.ImageSharp.PixelFormats /// Enumerates the various alpha composition modes. /// public enum PixelAlphaCompositionMode - { + { /// /// returns the destination over the source. /// - SrcOver = 0, - + SrcOver = 0, + /// /// returns the source colors. /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index 1d3cb53afc..0cf8d6bbbf 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -1,3810 +1,3810 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -// -using System; -using System.Numerics; -using System.Buffers; - -using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; - -namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders -{ - /// - /// Collection of Porter Duff alpha blending functions applying different composition models. - /// - /// - /// These functions are designed to be a general solution for all color cases, - /// that is, they take in account the alpha value of both the backdrop - /// and source, and there's no need to alpha-premultiply neither the backdrop - /// nor the source. - /// Note there are faster functions for when the backdrop color is known - /// to be opaque - /// - internal static class DefaultPixelBlenders - where TPixel : struct, IPixel - { - - internal class NormalSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrc Instance { get; } = new NormalSrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class AddSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrc Instance { get; } = new AddSrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class ScreenSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrc Instance { get; } = new ScreenSrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class LightenSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrc Instance { get; } = new LightenSrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlaySrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class HardLightSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrc Instance { get; } = new HardLightSrc(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class MultiplySrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcAtop Instance { get; } = new MultiplySrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class SubtractSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcAtop Instance { get; } = new SubtractSrcAtop(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class MultiplyDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestOver Instance { get; } = new MultiplyDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - 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, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class SubtractDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestOver Instance { get; } = new SubtractDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class DarkenDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestOver Instance { get; } = new DarkenDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class OverlayDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestOver Instance { get; } = new OverlayDestOver(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class NormalDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestIn Instance { get; } = new NormalDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class AddDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestIn Instance { get; } = new AddDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class ScreenDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestIn Instance { get; } = new ScreenDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class LightenDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestIn Instance { get; } = new LightenDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class HardLightDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestIn Instance { get; } = new HardLightDestIn(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class MultiplyDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestOut Instance { get; } = new MultiplyDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class SubtractDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestOut Instance { get; } = new SubtractDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class DarkenDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestOut Instance { get; } = new DarkenDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class OverlayDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestOut Instance { get; } = new OverlayDestOut(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class NormalClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalClear Instance { get; } = new NormalClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class AddClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddClear Instance { get; } = new AddClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class ScreenClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenClear Instance { get; } = new ScreenClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class LightenClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenClear Instance { get; } = new LightenClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class HardLightClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightClear Instance { get; } = new HardLightClear(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.NormalXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class MultiplyXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyXor Instance { get; } = new MultiplyXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.MultiplyXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.AddXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class SubtractXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractXor Instance { get; } = new SubtractXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.SubtractXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.ScreenXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class DarkenXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenXor Instance { get; } = new DarkenXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.DarkenXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.LightenXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal class OverlayXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayXor Instance { get; } = new OverlayXor(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.OverlayXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - internal 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) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.HardLightXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// +using System; +using System.Numerics; +using System.Buffers; + +using SixLabors.ImageSharp.Memory; +using SixLabors.Memory; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders +{ + /// + /// Collection of Porter Duff alpha blending functions applying different composition models. + /// + /// + /// These functions are designed to be a general solution for all color cases, + /// that is, they take in account the alpha value of both the backdrop + /// and source, and there's no need to alpha-premultiply neither the backdrop + /// nor the source. + /// Note there are faster functions for when the backdrop color is known + /// to be opaque + /// + internal static class DefaultPixelBlenders + where TPixel : struct, IPixel + { + + internal class NormalSrc : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static NormalSrc Instance { get; } = new NormalSrc(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.NormalSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class AddSrc : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static AddSrc Instance { get; } = new AddSrc(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.AddSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class ScreenSrc : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static ScreenSrc Instance { get; } = new ScreenSrc(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.DarkenSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class LightenSrc : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static LightenSrc Instance { get; } = new LightenSrc(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.LightenSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.OverlaySrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class HardLightSrc : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static HardLightSrc Instance { get; } = new HardLightSrc(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.HardLightSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.NormalSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class MultiplySrcAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static MultiplySrcAtop Instance { get; } = new MultiplySrcAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.AddSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class SubtractSrcAtop : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static SubtractSrcAtop Instance { get; } = new SubtractSrcAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.LightenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.NormalSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.AddSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.LightenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.NormalSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.AddSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.LightenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.NormalSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.AddSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.LightenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.NormalDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.MultiplyDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.AddDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.SubtractDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.ScreenDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.DarkenDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.LightenDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.OverlayDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.HardLightDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.NormalDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.AddDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.SubtractDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.ScreenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.DarkenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.LightenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.OverlayDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.HardLightDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.NormalDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class MultiplyDestOver : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static MultiplyDestOver Instance { get; } = new MultiplyDestOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.AddDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + 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, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class SubtractDestOver : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static SubtractDestOver Instance { get; } = new SubtractDestOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.SubtractDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.ScreenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class DarkenDestOver : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static DarkenDestOver Instance { get; } = new DarkenDestOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.DarkenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.LightenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class OverlayDestOver : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static OverlayDestOver Instance { get; } = new OverlayDestOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.OverlayDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.HardLightDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class NormalDestIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static NormalDestIn Instance { get; } = new NormalDestIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.NormalDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class AddDestIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static AddDestIn Instance { get; } = new AddDestIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.AddDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.SubtractDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class ScreenDestIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static ScreenDestIn Instance { get; } = new ScreenDestIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.ScreenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.DarkenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class LightenDestIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static LightenDestIn Instance { get; } = new LightenDestIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.LightenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.OverlayDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class HardLightDestIn : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static HardLightDestIn Instance { get; } = new HardLightDestIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.HardLightDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.NormalDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class MultiplyDestOut : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static MultiplyDestOut Instance { get; } = new MultiplyDestOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.AddDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class SubtractDestOut : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static SubtractDestOut Instance { get; } = new SubtractDestOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.SubtractDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.ScreenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class DarkenDestOut : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static DarkenDestOut Instance { get; } = new DarkenDestOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.DarkenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.LightenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class OverlayDestOut : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static OverlayDestOut Instance { get; } = new OverlayDestOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.OverlayDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.HardLightDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class NormalClear : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static NormalClear Instance { get; } = new NormalClear(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.NormalClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.MultiplyClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class AddClear : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static AddClear Instance { get; } = new AddClear(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.AddClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.SubtractClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class ScreenClear : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static ScreenClear Instance { get; } = new ScreenClear(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.ScreenClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.DarkenClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class LightenClear : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static LightenClear Instance { get; } = new LightenClear(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.LightenClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.OverlayClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class HardLightClear : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static HardLightClear Instance { get; } = new HardLightClear(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.HardLightClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.NormalXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class MultiplyXor : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static MultiplyXor Instance { get; } = new MultiplyXor(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.MultiplyXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.AddXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class SubtractXor : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static SubtractXor Instance { get; } = new SubtractXor(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.SubtractXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.ScreenXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class DarkenXor : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static DarkenXor Instance { get; } = new DarkenXor(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.DarkenXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.LightenXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal class OverlayXor : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static OverlayXor Instance { get; } = new OverlayXor(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.OverlayXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + internal 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) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.HardLightXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + + } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index b7ea7a9d43..2cca55e4c3 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -1,114 +1,114 @@ -<# -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. -#> -<#@ template debug="false" hostspecific="false" language="C#" #> -<#@ assembly name="System.Core" #> -<#@ import namespace="System.Linq" #> -<#@ import namespace="System.Text" #> -<#@ import namespace="System.Collections.Generic" #> -<#@ output extension=".cs" #> -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -// -using System; -using System.Numerics; -using System.Buffers; - -using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; - -namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders -{ - /// - /// Collection of Porter Duff alpha blending functions applying different composition models. - /// - /// - /// These functions are designed to be a general solution for all color cases, - /// that is, they take in account the alpha value of both the backdrop - /// and source, and there's no need to alpha-premultiply neither the backdrop - /// nor the source. - /// Note there are faster functions for when the backdrop color is known - /// to be opaque - /// - internal static class DefaultPixelBlenders - where TPixel : struct, IPixel - { - -<# - string[] composers = new []{ - "Src", - "SrcAtop", - "SrcOver", - "SrcIn", - "SrcOut", - "Dest", - "DestAtop", - "DestOver", - "DestIn", - "DestOut", - "Clear", - "Xor", - }; - - string[] blenders = new []{ - "Normal", - "Multiply", - "Add", - "Subtract", - "Screen", - "Darken", - "Lighten", - "Overlay", - "HardLight" - }; - - foreach(var composer in composers) { - foreach(var blender in blenders) { - - string blender_composer= $"{blender}{composer}"; - -#> - internal class <#= blender_composer#> : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static <#=blender_composer#> Instance { get; } = new <#=blender_composer#>(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - TPixel dest = default; - dest.FromScaledVector4(PorterDuffFunctions.<#=blender_composer#>(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - -<# - } - } - -#> - } +<# +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// +using System; +using System.Numerics; +using System.Buffers; + +using SixLabors.ImageSharp.Memory; +using SixLabors.Memory; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders +{ + /// + /// Collection of Porter Duff alpha blending functions applying different composition models. + /// + /// + /// These functions are designed to be a general solution for all color cases, + /// that is, they take in account the alpha value of both the backdrop + /// and source, and there's no need to alpha-premultiply neither the backdrop + /// nor the source. + /// Note there are faster functions for when the backdrop color is known + /// to be opaque + /// + internal static class DefaultPixelBlenders + where TPixel : struct, IPixel + { + +<# + string[] composers = new []{ + "Src", + "SrcAtop", + "SrcOver", + "SrcIn", + "SrcOut", + "Dest", + "DestAtop", + "DestOver", + "DestIn", + "DestOut", + "Clear", + "Xor", + }; + + string[] blenders = new []{ + "Normal", + "Multiply", + "Add", + "Subtract", + "Screen", + "Darken", + "Lighten", + "Overlay", + "HardLight" + }; + + foreach(var composer in composers) { + foreach(var blender in blenders) { + + string blender_composer= $"{blender}{composer}"; + +#> + internal class <#= blender_composer#> : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static <#=blender_composer#> Instance { get; } = new <#=blender_composer#>(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + TPixel dest = default; + dest.FromScaledVector4(PorterDuffFunctions.<#=blender_composer#>(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + +<# + } + } + +#> + } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index 64148746e0..a99deb535a 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -1,2162 +1,2162 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -// - - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders -{ - internal static partial class PorterDuffFunctions - { - - - - - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 NormalSrc(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return source; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 NormalSrcAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(backdrop, source, Normal(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 NormalSrcOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(backdrop, source, Normal(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 NormalSrcIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(backdrop, source, Normal(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 NormalSrcOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 NormalDest(Vector4 backdrop, Vector4 source, float opacity) - { - return backdrop; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 NormalDestAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(source, backdrop, Normal(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 NormalDestOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(source, backdrop, Normal(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 NormalDestIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(source, backdrop, Normal(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 NormalDestOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(source, backdrop); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 NormalXor(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Xor(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 NormalClear(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Clear(backdrop, source); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalSrc(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalSrcAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalSrcOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalSrcIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalSrcOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalDest(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalDestAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalDestOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalDestIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalDestOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalClear(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalXor(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(NormalXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 MultiplySrc(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return source; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 MultiplySrcAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(backdrop, source, Multiply(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 MultiplySrcOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(backdrop, source, Multiply(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 MultiplySrcIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(backdrop, source, Multiply(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 MultiplySrcOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 MultiplyDest(Vector4 backdrop, Vector4 source, float opacity) - { - return backdrop; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 MultiplyDestAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(source, backdrop, Multiply(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 MultiplyDestOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(source, backdrop, Multiply(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 MultiplyDestIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(source, backdrop, Multiply(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 MultiplyDestOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(source, backdrop); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 MultiplyXor(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Xor(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 MultiplyClear(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Clear(backdrop, source); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplySrc(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplySrcAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplySrcOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplySrcIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplySrcOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplyDest(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplyDestAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplyDestOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplyDestIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplyDestOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplyClear(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplyXor(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(MultiplyXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 AddSrc(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return source; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 AddSrcAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(backdrop, source, Add(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 AddSrcOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(backdrop, source, Add(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 AddSrcIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(backdrop, source, Add(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 AddSrcOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 AddDest(Vector4 backdrop, Vector4 source, float opacity) - { - return backdrop; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 AddDestAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(source, backdrop, Add(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 AddDestOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(source, backdrop, Add(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 AddDestIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(source, backdrop, Add(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 AddDestOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(source, backdrop); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 AddXor(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Xor(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 AddClear(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Clear(backdrop, source); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddSrc(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddSrcAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddSrcOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddSrcIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddSrcOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddDest(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddDestAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddDestOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddDestIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddDestOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddClear(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddXor(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(AddXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 SubtractSrc(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return source; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 SubtractSrcAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(backdrop, source, Subtract(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 SubtractSrcOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(backdrop, source, Subtract(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 SubtractSrcIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(backdrop, source, Subtract(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 SubtractSrcOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 SubtractDest(Vector4 backdrop, Vector4 source, float opacity) - { - return backdrop; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 SubtractDestAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(source, backdrop, Subtract(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 SubtractDestOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(source, backdrop, Subtract(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 SubtractDestIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(source, backdrop, Subtract(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 SubtractDestOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(source, backdrop); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 SubtractXor(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Xor(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 SubtractClear(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Clear(backdrop, source); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubtractSrc(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubtractSrcAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubtractSrcOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubtractSrcIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubtractSrcOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubtractDest(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubtractDestAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubtractDestOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubtractDestIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubtractDestOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubtractClear(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubtractXor(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(SubtractXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 ScreenSrc(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return source; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 ScreenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(backdrop, source, Screen(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 ScreenSrcOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(backdrop, source, Screen(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 ScreenSrcIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(backdrop, source, Screen(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 ScreenSrcOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 ScreenDest(Vector4 backdrop, Vector4 source, float opacity) - { - return backdrop; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 ScreenDestAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(source, backdrop, Screen(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 ScreenDestOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(source, backdrop, Screen(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 ScreenDestIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(source, backdrop, Screen(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 ScreenDestOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(source, backdrop); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 ScreenXor(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Xor(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 ScreenClear(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Clear(backdrop, source); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenSrc(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenSrcAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenSrcOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenSrcIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenSrcOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenDest(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenDestAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenDestOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenDestIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenDestOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenClear(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenXor(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(ScreenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 DarkenSrc(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return source; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 DarkenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(backdrop, source, Darken(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 DarkenSrcOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(backdrop, source, Darken(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 DarkenSrcIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(backdrop, source, Darken(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 DarkenSrcOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 DarkenDest(Vector4 backdrop, Vector4 source, float opacity) - { - return backdrop; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 DarkenDestAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(source, backdrop, Darken(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 DarkenDestOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(source, backdrop, Darken(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 DarkenDestIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(source, backdrop, Darken(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 DarkenDestOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(source, backdrop); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 DarkenXor(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Xor(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 DarkenClear(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Clear(backdrop, source); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenSrc(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenSrcAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenSrcOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenSrcIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenSrcOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenDest(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenDestAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenDestOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenDestIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenDestOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenClear(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenXor(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(DarkenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 LightenSrc(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return source; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 LightenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(backdrop, source, Lighten(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 LightenSrcOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(backdrop, source, Lighten(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 LightenSrcIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(backdrop, source, Lighten(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 LightenSrcOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 LightenDest(Vector4 backdrop, Vector4 source, float opacity) - { - return backdrop; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 LightenDestAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(source, backdrop, Lighten(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 LightenDestOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(source, backdrop, Lighten(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 LightenDestIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(source, backdrop, Lighten(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 LightenDestOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(source, backdrop); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 LightenXor(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Xor(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 LightenClear(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Clear(backdrop, source); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenSrc(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenSrcAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenSrcOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenSrcIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenSrcOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenDest(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenDestAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenDestOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenDestIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenDestOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenClear(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenXor(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(LightenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 OverlaySrc(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return source; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 OverlaySrcAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(backdrop, source, Overlay(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 OverlaySrcOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(backdrop, source, Overlay(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 OverlaySrcIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(backdrop, source, Overlay(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 OverlaySrcOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 OverlayDest(Vector4 backdrop, Vector4 source, float opacity) - { - return backdrop; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 OverlayDestAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(source, backdrop, Overlay(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 OverlayDestOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(source, backdrop, Overlay(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 OverlayDestIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(source, backdrop, Overlay(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 OverlayDestOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(source, backdrop); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 OverlayXor(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Xor(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 OverlayClear(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Clear(backdrop, source); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlaySrc(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlaySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlaySrcAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlaySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlaySrcOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlaySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlaySrcIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlaySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlaySrcOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlaySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlayDest(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlayDestAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlayDestOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlayDestIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlayDestOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlayClear(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlayXor(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlayXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 HardLightSrc(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return source; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 HardLightSrcAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(backdrop, source, HardLight(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 HardLightSrcOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(backdrop, source, HardLight(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 HardLightSrcIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(backdrop, source, HardLight(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 HardLightSrcOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 HardLightDest(Vector4 backdrop, Vector4 source, float opacity) - { - return backdrop; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 HardLightDestAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(source, backdrop, HardLight(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 HardLightDestOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(source, backdrop, HardLight(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 HardLightDestIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(source, backdrop, HardLight(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 HardLightDestOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(source, backdrop); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 HardLightXor(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Xor(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 HardLightClear(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Clear(backdrop, source); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel HardLightSrc(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel HardLightSrcAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel HardLightSrcOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel HardLightSrcIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel HardLightSrcOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel HardLightDest(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel HardLightDestAtop(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel HardLightDestOver(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel HardLightDestIn(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel HardLightDestOut(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel HardLightClear(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel HardLightXor(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(HardLightXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders +{ + internal static partial class PorterDuffFunctions + { + + + + + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 NormalSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return source; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 NormalSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(backdrop, source, Normal(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 NormalSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(backdrop, source, Normal(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 NormalSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(backdrop, source, Normal(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 NormalSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 NormalDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 NormalDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(source, backdrop, Normal(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 NormalDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(source, backdrop, Normal(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 NormalDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(source, backdrop, Normal(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 NormalDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(source, backdrop); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 NormalXor(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Xor(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 NormalClear(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Clear(backdrop, source); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(NormalSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(NormalSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(NormalSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(NormalSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(NormalSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(NormalDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(NormalDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(NormalDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(NormalDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(NormalDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(NormalClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(NormalXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 MultiplySrc(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return source; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 MultiplySrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(backdrop, source, Multiply(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 MultiplySrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(backdrop, source, Multiply(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 MultiplySrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(backdrop, source, Multiply(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 MultiplySrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 MultiplyDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 MultiplyDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(source, backdrop, Multiply(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 MultiplyDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(source, backdrop, Multiply(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 MultiplyDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(source, backdrop, Multiply(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 MultiplyDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(source, backdrop); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 MultiplyXor(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Xor(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 MultiplyClear(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Clear(backdrop, source); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplySrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(MultiplySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplySrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(MultiplySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplySrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(MultiplySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplySrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(MultiplySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplySrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(MultiplySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplyDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(MultiplyDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplyDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(MultiplyDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplyDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(MultiplyDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplyDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(MultiplyDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplyDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(MultiplyDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplyClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(MultiplyClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplyXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(MultiplyXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 AddSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return source; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 AddSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(backdrop, source, Add(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 AddSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(backdrop, source, Add(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 AddSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(backdrop, source, Add(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 AddSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 AddDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 AddDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(source, backdrop, Add(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 AddDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(source, backdrop, Add(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 AddDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(source, backdrop, Add(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 AddDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(source, backdrop); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 AddXor(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Xor(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 AddClear(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Clear(backdrop, source); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(AddSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(AddSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(AddSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(AddSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(AddSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(AddDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(AddDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(AddDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(AddDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(AddDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(AddClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(AddXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 SubtractSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return source; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 SubtractSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(backdrop, source, Subtract(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 SubtractSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(backdrop, source, Subtract(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 SubtractSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(backdrop, source, Subtract(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 SubtractSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 SubtractDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 SubtractDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(source, backdrop, Subtract(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 SubtractDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(source, backdrop, Subtract(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 SubtractDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(source, backdrop, Subtract(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 SubtractDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(source, backdrop); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 SubtractXor(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Xor(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 SubtractClear(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Clear(backdrop, source); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubtractSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(SubtractSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubtractSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(SubtractSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubtractSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(SubtractSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubtractSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(SubtractSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubtractSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(SubtractSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubtractDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(SubtractDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubtractDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(SubtractDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubtractDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(SubtractDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubtractDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(SubtractDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubtractDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(SubtractDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubtractClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(SubtractClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubtractXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(SubtractXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 ScreenSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return source; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 ScreenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(backdrop, source, Screen(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 ScreenSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(backdrop, source, Screen(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 ScreenSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(backdrop, source, Screen(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 ScreenSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 ScreenDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 ScreenDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(source, backdrop, Screen(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 ScreenDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(source, backdrop, Screen(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 ScreenDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(source, backdrop, Screen(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 ScreenDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(source, backdrop); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 ScreenXor(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Xor(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 ScreenClear(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Clear(backdrop, source); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(ScreenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(ScreenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(ScreenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(ScreenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(ScreenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(ScreenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(ScreenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(ScreenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(ScreenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(ScreenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(ScreenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(ScreenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 DarkenSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return source; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 DarkenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(backdrop, source, Darken(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 DarkenSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(backdrop, source, Darken(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 DarkenSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(backdrop, source, Darken(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 DarkenSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 DarkenDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 DarkenDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(source, backdrop, Darken(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 DarkenDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(source, backdrop, Darken(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 DarkenDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(source, backdrop, Darken(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 DarkenDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(source, backdrop); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 DarkenXor(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Xor(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 DarkenClear(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Clear(backdrop, source); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(DarkenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(DarkenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(DarkenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(DarkenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(DarkenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(DarkenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(DarkenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(DarkenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(DarkenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(DarkenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(DarkenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(DarkenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 LightenSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return source; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 LightenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(backdrop, source, Lighten(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 LightenSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(backdrop, source, Lighten(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 LightenSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(backdrop, source, Lighten(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 LightenSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 LightenDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 LightenDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(source, backdrop, Lighten(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 LightenDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(source, backdrop, Lighten(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 LightenDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(source, backdrop, Lighten(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 LightenDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(source, backdrop); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 LightenXor(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Xor(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 LightenClear(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Clear(backdrop, source); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(LightenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(LightenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(LightenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(LightenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(LightenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(LightenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(LightenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(LightenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(LightenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(LightenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(LightenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(LightenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 OverlaySrc(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return source; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 OverlaySrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(backdrop, source, Overlay(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 OverlaySrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(backdrop, source, Overlay(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 OverlaySrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(backdrop, source, Overlay(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 OverlaySrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 OverlayDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 OverlayDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(source, backdrop, Overlay(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 OverlayDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(source, backdrop, Overlay(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 OverlayDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(source, backdrop, Overlay(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 OverlayDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(source, backdrop); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 OverlayXor(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Xor(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 OverlayClear(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Clear(backdrop, source); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlaySrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(OverlaySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlaySrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(OverlaySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlaySrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(OverlaySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlaySrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(OverlaySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlaySrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(OverlaySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlayDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(OverlayDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlayDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(OverlayDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlayDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(OverlayDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlayDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(OverlayDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlayDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(OverlayDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlayClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(OverlayClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlayXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(OverlayXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 HardLightSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return source; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 HardLightSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(backdrop, source, HardLight(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 HardLightSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(backdrop, source, HardLight(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 HardLightSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(backdrop, source, HardLight(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 HardLightSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 HardLightDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 HardLightDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(source, backdrop, HardLight(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 HardLightDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(source, backdrop, HardLight(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 HardLightDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(source, backdrop, HardLight(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 HardLightDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(source, backdrop); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 HardLightXor(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Xor(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 HardLightClear(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Clear(backdrop, source); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(HardLightSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(HardLightSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(HardLightSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(HardLightSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(HardLightSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(HardLightDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(HardLightDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(HardLightDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(HardLightDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(HardLightDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(HardLightClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(HardLightXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + + } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index e21a78031f..c8345cfb9f 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -1,183 +1,183 @@ -<# -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. -#> -<#@ template debug="false" hostspecific="false" language="C#" #> -<#@ assembly name="System.Core" #> -<#@ import namespace="System.Linq" #> -<#@ import namespace="System.Text" #> -<#@ import namespace="System.Collections.Generic" #> -<#@ output extension=".cs" #> -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -// - -<# -// Note use of MethodImplOptions.NoInlining. We have tests that are failing on certain architectures when -// AggresiveInlining is used. Confirmed on Intel i7-6600U in 64bit. -#> - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders -{ - internal static partial class PorterDuffFunctions - { - -<# void GeneratePixelBlenders(string blender) { #> - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 <#=blender#>Src(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return source; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 <#=blender#>SrcAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(backdrop, source, <#=blender#>(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 <#=blender#>SrcOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(backdrop, source, <#=blender#>(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 <#=blender#>SrcIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(backdrop, source, <#=blender#>(backdrop, source)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 <#=blender#>SrcOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 <#=blender#>Dest(Vector4 backdrop, Vector4 source, float opacity) - { - return backdrop; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 <#=blender#>DestAtop(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Atop(source, backdrop, <#=blender#>(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 <#=blender#>DestOver(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Over(source, backdrop, <#=blender#>(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 <#=blender#>DestIn(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return In(source, backdrop, <#=blender#>(source, backdrop)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 <#=blender#>DestOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(source, backdrop); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 <#=blender#>Xor(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Xor(backdrop, source); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Vector4 <#=blender#>Clear(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Clear(backdrop, source); - } -<# } #> - - -<# void GenerateGenericPixelBlender(string blender, string composer) { #> - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel <#=blender#><#=composer#>(TPixel backdrop, TPixel source, float opacity) - where TPixel : struct, IPixel - { - opacity = opacity.Clamp(0, 1); - TPixel dest = default; - dest.FromScaledVector4(<#=blender#><#=composer#>(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; - } - -<# } #> - -<# - -string[] composers = new []{ - "Src", - "SrcAtop", - "SrcOver", - "SrcIn", - "SrcOut", - "Dest", - "DestAtop", - "DestOver", - "DestIn", - "DestOut", - "Clear", - "Xor", -}; - -string[] blenders = new []{ - "Normal", - "Multiply", - "Add", - "Subtract", - "Screen", - "Darken", - "Lighten", - "Overlay", - "HardLight" -}; - - foreach(var blender in blenders) - { - GeneratePixelBlenders(blender); - - foreach(var composer in composers) - { - GenerateGenericPixelBlender(blender,composer); - } - } - -#> - } +<# +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +<# +// Note use of MethodImplOptions.NoInlining. We have tests that are failing on certain architectures when +// AggresiveInlining is used. Confirmed on Intel i7-6600U in 64bit. +#> + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders +{ + internal static partial class PorterDuffFunctions + { + +<# void GeneratePixelBlenders(string blender) { #> + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 <#=blender#>Src(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return source; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 <#=blender#>SrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(backdrop, source, <#=blender#>(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 <#=blender#>SrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(backdrop, source, <#=blender#>(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 <#=blender#>SrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(backdrop, source, <#=blender#>(backdrop, source)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 <#=blender#>SrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 <#=blender#>Dest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 <#=blender#>DestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Atop(source, backdrop, <#=blender#>(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 <#=blender#>DestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Over(source, backdrop, <#=blender#>(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 <#=blender#>DestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return In(source, backdrop, <#=blender#>(source, backdrop)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 <#=blender#>DestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Out(source, backdrop); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 <#=blender#>Xor(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Xor(backdrop, source); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Vector4 <#=blender#>Clear(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + + return Clear(backdrop, source); + } +<# } #> + + +<# void GenerateGenericPixelBlender(string blender, string composer) { #> + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel <#=blender#><#=composer#>(TPixel backdrop, TPixel source, float opacity) + where TPixel : struct, IPixel + { + opacity = opacity.Clamp(0, 1); + TPixel dest = default; + dest.FromScaledVector4(<#=blender#><#=composer#>(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + return dest; + } + +<# } #> + +<# + +string[] composers = new []{ + "Src", + "SrcAtop", + "SrcOver", + "SrcIn", + "SrcOut", + "Dest", + "DestAtop", + "DestOver", + "DestIn", + "DestOut", + "Clear", + "Xor", +}; + +string[] blenders = new []{ + "Normal", + "Multiply", + "Add", + "Subtract", + "Screen", + "Darken", + "Lighten", + "Overlay", + "HardLight" +}; + + foreach(var blender in blenders) + { + GeneratePixelBlenders(blender); + + foreach(var composer in composers) + { + GenerateGenericPixelBlender(blender,composer); + } + } + +#> + } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index 9d0e9d04d3..9111520a02 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -1,238 +1,238 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders -{ - /// - /// Collection of Porter Duff Color Blending and Alpha Composition Functions. - /// - /// - /// These functions are designed to be a general solution for all color cases, - /// that is, they take in account the alpha value of both the backdrop - /// and source, and there's no need to alpha-premultiply neither the backdrop - /// nor the source. - /// Note there are faster functions for when the backdrop color is known - /// to be opaque - /// - internal static partial class PorterDuffFunctions - { - /// - /// Source over backdrop - /// - /// Backdrop color - /// Source color - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Normal(Vector4 backdrop, Vector4 source) - { - return source; - } - - /// - /// Source multiplied by backdrop - /// - /// Backdrop color - /// Source color - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Multiply(Vector4 backdrop, Vector4 source) - { - return backdrop * source; - } - - /// - /// Source added to backdrop - /// - /// Backdrop color - /// Source color - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Add(Vector4 backdrop, Vector4 source) - { - return Vector4.Min(Vector4.One, backdrop + source); - } - - /// - /// Source subtracted from backdrop - /// - /// Backdrop color - /// Source color - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Subtract(Vector4 backdrop, Vector4 source) - { - return Vector4.Max(Vector4.Zero, backdrop - source); - } - - /// - /// Complement of source multiplied by the complement of backdrop - /// - /// Backdrop color - /// Source color - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Screen(Vector4 backdrop, Vector4 source) - { - return Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source)); - } - - /// - /// Per element, chooses the smallest value of source and backdrop - /// - /// Backdrop color - /// Source color - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Darken(Vector4 backdrop, Vector4 source) - { - return Vector4.Min(backdrop, source); - } - - /// - /// Per element, chooses the largest value of source and backdrop - /// - /// Backdrop color - /// Source color - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Lighten(Vector4 backdrop, Vector4 source) - { - return Vector4.Max(backdrop, source); - } - - /// - /// Overlays source over backdrop - /// - /// Backdrop color - /// Source color - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Overlay(Vector4 backdrop, Vector4 source) - { - float cr = OverlayValueFunction(backdrop.X, source.X); - float cg = OverlayValueFunction(backdrop.Y, source.Y); - float cb = OverlayValueFunction(backdrop.Z, source.Z); - - return Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0)); - } - - /// - /// Hard light effect - /// - /// Backdrop color - /// Source color - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 HardLight(Vector4 backdrop, Vector4 source) - { - float cr = OverlayValueFunction(source.X, backdrop.X); - float cg = OverlayValueFunction(source.Y, backdrop.Y); - float cb = OverlayValueFunction(source.Z, backdrop.Z); - - return Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0)); - } - - /// - /// Helper function for Overlay andHardLight modes - /// - /// Backdrop color element - /// Source color element - /// Overlay value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float OverlayValueFunction(float backdrop, float source) - { - return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Over(Vector4 dst, Vector4 src, Vector4 blend) - { - // calculate weights - float blendW = dst.W * src.W; - float dstW = dst.W - blendW; - float srcW = src.W - blendW; - - // calculate final alpha - float alpha = dstW + srcW + blendW; - - // calculate final color - Vector4 color = (dst * dstW) + (src * srcW) + (blend * blendW); - - // unpremultiply - color /= MathF.Max(alpha, Constants.Epsilon); - color.W = alpha; - - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Atop(Vector4 dst, Vector4 src, Vector4 blend) - { - // calculate weights - float blendW = dst.W * src.W; - float dstW = dst.W - blendW; - - // calculate final alpha - float alpha = dstW + blendW; - - // calculate final color - Vector4 color = (dst * dstW) + (blend * blendW); - - // unpremultiply - color /= MathF.Max(alpha, Constants.Epsilon); - color.W = alpha; - - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 In(Vector4 dst, Vector4 src, Vector4 blend) - { - float alpha = dst.W * src.W; - - Vector4 color = src * alpha; // premultiply - color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply - color.W = alpha; - - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Out(Vector4 dst, Vector4 src) - { - float alpha = (1 - dst.W) * src.W; - - Vector4 color = src * alpha; // premultiply - color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply - color.W = alpha; - - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Xor(Vector4 dst, Vector4 src) - { - float srcW = 1 - dst.W; - float dstW = 1 - src.W; - - float alpha = (src.W * srcW) + (dst.W * dstW); - Vector4 color = (src.W * src * srcW) + (dst.W * dst * dstW); - - // unpremultiply - color /= MathF.Max(alpha, Constants.Epsilon); - color.W = alpha; - - return color; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector4 Clear(Vector4 backdrop, Vector4 source) - { - return Vector4.Zero; - } - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders +{ + /// + /// Collection of Porter Duff Color Blending and Alpha Composition Functions. + /// + /// + /// These functions are designed to be a general solution for all color cases, + /// that is, they take in account the alpha value of both the backdrop + /// and source, and there's no need to alpha-premultiply neither the backdrop + /// nor the source. + /// Note there are faster functions for when the backdrop color is known + /// to be opaque + /// + internal static partial class PorterDuffFunctions + { + /// + /// Source over backdrop + /// + /// Backdrop color + /// Source color + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Normal(Vector4 backdrop, Vector4 source) + { + return source; + } + + /// + /// Source multiplied by backdrop + /// + /// Backdrop color + /// Source color + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Multiply(Vector4 backdrop, Vector4 source) + { + return backdrop * source; + } + + /// + /// Source added to backdrop + /// + /// Backdrop color + /// Source color + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Add(Vector4 backdrop, Vector4 source) + { + return Vector4.Min(Vector4.One, backdrop + source); + } + + /// + /// Source subtracted from backdrop + /// + /// Backdrop color + /// Source color + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Subtract(Vector4 backdrop, Vector4 source) + { + return Vector4.Max(Vector4.Zero, backdrop - source); + } + + /// + /// Complement of source multiplied by the complement of backdrop + /// + /// Backdrop color + /// Source color + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Screen(Vector4 backdrop, Vector4 source) + { + return Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source)); + } + + /// + /// Per element, chooses the smallest value of source and backdrop + /// + /// Backdrop color + /// Source color + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Darken(Vector4 backdrop, Vector4 source) + { + return Vector4.Min(backdrop, source); + } + + /// + /// Per element, chooses the largest value of source and backdrop + /// + /// Backdrop color + /// Source color + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Lighten(Vector4 backdrop, Vector4 source) + { + return Vector4.Max(backdrop, source); + } + + /// + /// Overlays source over backdrop + /// + /// Backdrop color + /// Source color + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Overlay(Vector4 backdrop, Vector4 source) + { + float cr = OverlayValueFunction(backdrop.X, source.X); + float cg = OverlayValueFunction(backdrop.Y, source.Y); + float cb = OverlayValueFunction(backdrop.Z, source.Z); + + return Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0)); + } + + /// + /// Hard light effect + /// + /// Backdrop color + /// Source color + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLight(Vector4 backdrop, Vector4 source) + { + float cr = OverlayValueFunction(source.X, backdrop.X); + float cg = OverlayValueFunction(source.Y, backdrop.Y); + float cb = OverlayValueFunction(source.Z, backdrop.Z); + + return Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0)); + } + + /// + /// Helper function for Overlay andHardLight modes + /// + /// Backdrop color element + /// Source color element + /// Overlay value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float OverlayValueFunction(float backdrop, float source) + { + return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Over(Vector4 dst, Vector4 src, Vector4 blend) + { + // calculate weights + float blendW = dst.W * src.W; + float dstW = dst.W - blendW; + float srcW = src.W - blendW; + + // calculate final alpha + float alpha = dstW + srcW + blendW; + + // calculate final color + Vector4 color = (dst * dstW) + (src * srcW) + (blend * blendW); + + // unpremultiply + color /= MathF.Max(alpha, Constants.Epsilon); + color.W = alpha; + + return color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Atop(Vector4 dst, Vector4 src, Vector4 blend) + { + // calculate weights + float blendW = dst.W * src.W; + float dstW = dst.W - blendW; + + // calculate final alpha + float alpha = dstW + blendW; + + // calculate final color + Vector4 color = (dst * dstW) + (blend * blendW); + + // unpremultiply + color /= MathF.Max(alpha, Constants.Epsilon); + color.W = alpha; + + return color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 In(Vector4 dst, Vector4 src, Vector4 blend) + { + float alpha = dst.W * src.W; + + Vector4 color = src * alpha; // premultiply + color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply + color.W = alpha; + + return color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Out(Vector4 dst, Vector4 src) + { + float alpha = (1 - dst.W) * src.W; + + Vector4 color = src * alpha; // premultiply + color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply + color.W = alpha; + + return color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Xor(Vector4 dst, Vector4 src) + { + float srcW = 1 - dst.W; + float dstW = 1 - src.W; + + float alpha = (src.W * srcW) + (dst.W * dstW); + Vector4 color = (src.W * src * srcW) + (dst.W * dst * dstW); + + // unpremultiply + color /= MathF.Max(alpha, Constants.Epsilon); + color.W = alpha; + + return color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Clear(Vector4 backdrop, Vector4 source) + { + return Vector4.Zero; + } + } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index d2a6fbf501..77bee23937 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -1,170 +1,170 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; -using System.Numerics; - -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Abstract base class for calling pixel composition functions - /// - /// The type of the pixel - internal abstract class PixelBlender - where TPixel : struct, IPixel - { - /// - /// Blend 2 pixels together. - /// - /// The background color. - /// The source color. - /// - /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - /// The final pixel value after composition - public abstract TPixel Blend(TPixel background, TPixel source, float amount); - - /// - /// Blend 2 rows together. - /// - /// destination span - /// the background span - /// the source span - /// - /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - protected abstract void BlendFunction( - Span destination, - ReadOnlySpan background, - ReadOnlySpan source, - float amount); - - /// - /// Blend 2 rows together. - /// - /// destination span - /// the background span - /// the source span - /// - /// A span with values between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - protected abstract void BlendFunction( - Span destination, - ReadOnlySpan background, - ReadOnlySpan source, - ReadOnlySpan amount); - - /// - /// Blends 2 rows together - /// - /// to use internally - /// the destination span - /// the background span - /// the source span - /// - /// A span with values between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - public void Blend( - Configuration configuration, - Span destination, - ReadOnlySpan background, - ReadOnlySpan source, - ReadOnlySpan amount) - { - this.Blend(configuration, destination, background, source, amount); - } - - /// - /// Blends 2 rows together - /// - /// the pixel format of the source span - /// to use internally - /// the destination span - /// the background span - /// the source span - /// - /// A span with values between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - public void Blend( - Configuration configuration, - Span destination, - ReadOnlySpan background, - ReadOnlySpan source, - ReadOnlySpan amount) - where TPixelSrc : struct, IPixel - { - Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - - using (IMemoryOwner buffer = - configuration.MemoryAllocator.Allocate(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - ReadOnlySpan sourcePixels = background.Slice(0, background.Length); - PixelOperations.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale); - ReadOnlySpan sourcePixels1 = source.Slice(0, background.Length); - PixelOperations.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale); - - this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); - - Span sourceVectors = destinationSpan.Slice(0, background.Length); - PixelOperations.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); - } - } - - /// - /// Blends 2 rows together - /// - /// the pixel format of the source span - /// to use internally - /// the destination span - /// the background span - /// the source span - /// - /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - public void Blend( - Configuration configuration, - Span destination, - ReadOnlySpan background, - ReadOnlySpan source, - float amount) - where TPixelSrc : struct, IPixel - { - Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); - - using (IMemoryOwner buffer = - configuration.MemoryAllocator.Allocate(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - ReadOnlySpan sourcePixels = background.Slice(0, background.Length); - PixelOperations.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale); - ReadOnlySpan sourcePixels1 = source.Slice(0, background.Length); - PixelOperations.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale); - - this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); - - Span sourceVectors = destinationSpan.Slice(0, background.Length); - PixelOperations.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); - } - } - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Numerics; + +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal abstract class PixelBlender + where TPixel : struct, IPixel + { + /// + /// Blend 2 pixels together. + /// + /// The background color. + /// The source color. + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + /// The final pixel value after composition + public abstract TPixel Blend(TPixel background, TPixel source, float amount); + + /// + /// Blend 2 rows together. + /// + /// destination span + /// the background span + /// the source span + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + protected abstract void BlendFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount); + + /// + /// Blend 2 rows together. + /// + /// destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + protected abstract void BlendFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount); + + /// + /// Blends 2 rows together + /// + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount) + { + this.Blend(configuration, destination, background, source, amount); + } + + /// + /// Blends 2 rows together + /// + /// the pixel format of the source span + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount) + where TPixelSrc : struct, IPixel + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (IMemoryOwner buffer = + configuration.MemoryAllocator.Allocate(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + ReadOnlySpan sourcePixels = background.Slice(0, background.Length); + PixelOperations.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale); + ReadOnlySpan sourcePixels1 = source.Slice(0, background.Length); + PixelOperations.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale); + + this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); + + Span sourceVectors = destinationSpan.Slice(0, background.Length); + PixelOperations.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); + } + } + + /// + /// Blends 2 rows together + /// + /// the pixel format of the source span + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount) + where TPixelSrc : struct, IPixel + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + + using (IMemoryOwner buffer = + configuration.MemoryAllocator.Allocate(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + ReadOnlySpan sourcePixels = background.Slice(0, background.Length); + PixelOperations.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale); + ReadOnlySpan sourcePixels1 = source.Slice(0, background.Length); + PixelOperations.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale); + + this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); + + Span sourceVectors = destinationSpan.Slice(0, background.Length); + PixelOperations.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); + } + } + } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs index dcddadb6a1..63db674c8e 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs @@ -1,217 +1,217 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats.PixelBlenders; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Provides access to pixel blenders - /// - public partial class PixelOperations - where TPixel : struct, IPixel - { - /// - /// Find an instance of the pixel blender. - /// - /// the blending and composition to apply - /// A . - internal PixelBlender GetPixelBlender(GraphicsOptions options) - { - return this.GetPixelBlender(options.ColorBlendingMode, options.AlphaCompositionMode); - } - - /// - /// Find an instance of the pixel blender. - /// - /// The color blending mode to apply - /// The alpha composition mode to apply - /// A . - internal virtual PixelBlender GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) - { - switch (alphaMode) - { - case PixelAlphaCompositionMode.Clear: - switch (colorMode) - { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyClear.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddClear.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractClear.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenClear.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenClear.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenClear.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayClear.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightClear.Instance; - case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalClear.Instance; - } - - case PixelAlphaCompositionMode.Xor: - switch (colorMode) - { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyXor.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddXor.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractXor.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenXor.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenXor.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenXor.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayXor.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightXor.Instance; - case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalXor.Instance; - } - - case PixelAlphaCompositionMode.Src: - switch (colorMode) - { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrc.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrc.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrc.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrc.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrc.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrc.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrc.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrc.Instance; - case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalSrc.Instance; - } - - case PixelAlphaCompositionMode.SrcAtop: - switch (colorMode) - { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcAtop.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcAtop.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcAtop.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcAtop.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcAtop.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcAtop.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcAtop.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcAtop.Instance; - case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalSrcAtop.Instance; - } - - case PixelAlphaCompositionMode.SrcIn: - switch (colorMode) - { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcIn.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcIn.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcIn.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcIn.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcIn.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcIn.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcIn.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcIn.Instance; - case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalSrcIn.Instance; - } - - case PixelAlphaCompositionMode.SrcOut: - switch (colorMode) - { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcOut.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcOut.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcOut.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcOut.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcOut.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcOut.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcOut.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcOut.Instance; - case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalSrcOut.Instance; - } - - case PixelAlphaCompositionMode.Dest: - switch (colorMode) - { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDest.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDest.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDest.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDest.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDest.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDest.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDest.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDest.Instance; - case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalDest.Instance; - } - - case PixelAlphaCompositionMode.DestAtop: - switch (colorMode) - { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestAtop.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestAtop.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestAtop.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestAtop.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestAtop.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestAtop.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestAtop.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestAtop.Instance; - case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalDestAtop.Instance; - } - - case PixelAlphaCompositionMode.DestIn: - switch (colorMode) - { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestIn.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestIn.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestIn.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestIn.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestIn.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestIn.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestIn.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestIn.Instance; - case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalDestIn.Instance; - } - - case PixelAlphaCompositionMode.DestOut: - switch (colorMode) - { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestOut.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestOut.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestOut.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestOut.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestOut.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestOut.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestOut.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestOut.Instance; - case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalDestOut.Instance; - } - - case PixelAlphaCompositionMode.DestOver: - switch (colorMode) - { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestOver.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestOver.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestOver.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestOver.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestOver.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestOver.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestOver.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestOver.Instance; - case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalDestOver.Instance; - } - - case PixelAlphaCompositionMode.SrcOver: - default: - switch (colorMode) - { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcOver.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcOver.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcOver.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcOver.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcOver.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcOver.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcOver.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcOver.Instance; - case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalSrcOver.Instance; - } - } - } - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides access to pixel blenders + /// + public partial class PixelOperations + where TPixel : struct, IPixel + { + /// + /// Find an instance of the pixel blender. + /// + /// the blending and composition to apply + /// A . + internal PixelBlender GetPixelBlender(GraphicsOptions options) + { + return this.GetPixelBlender(options.ColorBlendingMode, options.AlphaCompositionMode); + } + + /// + /// Find an instance of the pixel blender. + /// + /// The color blending mode to apply + /// The alpha composition mode to apply + /// A . + internal virtual PixelBlender GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) + { + switch (alphaMode) + { + case PixelAlphaCompositionMode.Clear: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyClear.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddClear.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractClear.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenClear.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenClear.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenClear.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayClear.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightClear.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalClear.Instance; + } + + case PixelAlphaCompositionMode.Xor: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyXor.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddXor.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractXor.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenXor.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenXor.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenXor.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayXor.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightXor.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalXor.Instance; + } + + case PixelAlphaCompositionMode.Src: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrc.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrc.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrc.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrc.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrc.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrc.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrc.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrc.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalSrc.Instance; + } + + case PixelAlphaCompositionMode.SrcAtop: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcAtop.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcAtop.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcAtop.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcAtop.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcAtop.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcAtop.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcAtop.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcAtop.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalSrcAtop.Instance; + } + + case PixelAlphaCompositionMode.SrcIn: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcIn.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcIn.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcIn.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcIn.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcIn.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcIn.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcIn.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcIn.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalSrcIn.Instance; + } + + case PixelAlphaCompositionMode.SrcOut: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcOut.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcOut.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcOut.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcOut.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcOut.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcOut.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcOut.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcOut.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalSrcOut.Instance; + } + + case PixelAlphaCompositionMode.Dest: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDest.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDest.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDest.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDest.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDest.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDest.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDest.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDest.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalDest.Instance; + } + + case PixelAlphaCompositionMode.DestAtop: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestAtop.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestAtop.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestAtop.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestAtop.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestAtop.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestAtop.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestAtop.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestAtop.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalDestAtop.Instance; + } + + case PixelAlphaCompositionMode.DestIn: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestIn.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestIn.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestIn.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestIn.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestIn.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestIn.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestIn.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestIn.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalDestIn.Instance; + } + + case PixelAlphaCompositionMode.DestOut: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestOut.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestOut.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestOut.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestOut.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestOut.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestOut.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestOut.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestOut.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalDestOut.Instance; + } + + case PixelAlphaCompositionMode.DestOver: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestOver.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestOver.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestOver.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestOver.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestOver.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestOver.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestOver.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestOver.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalDestOver.Instance; + } + + case PixelAlphaCompositionMode.SrcOver: + default: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcOver.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcOver.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcOver.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcOver.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcOver.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcOver.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcOver.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcOver.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalSrcOver.Instance; + } + } + } + } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index befc9eeec2..67f0c39c58 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -4,6 +4,11 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; @@ -40,13 +45,13 @@ internal class BokehBlurProcessor : ImageProcessor /// /// The complex kernels to use to apply the blur for the current instance /// - private readonly IReadOnlyList complexKernels; + private readonly IReadOnlyList> complexKernels; /// /// The mapping of initialized complex kernels and parameters, to speed up the initialization of new instances /// - private static readonly Dictionary<(int, int), (IReadOnlyList>, float, IReadOnlyList)> Cache = - new Dictionary<(int, int), (IReadOnlyList>, float, IReadOnlyList)>(); + private static readonly Dictionary<(int, int), (IReadOnlyList>, float, IReadOnlyList>)> Cache = + new Dictionary<(int, int), (IReadOnlyList>, float, IReadOnlyList>)>(); /// /// Initializes a new instance of the class. @@ -64,7 +69,7 @@ public BokehBlurProcessor(int radius = 32, int components = 2) this.componentsCount = components; // Reuse the initialized values from the cache, if possible - if (Cache.TryGetValue((radius, components), out (IReadOnlyList>, float, IReadOnlyList) info)) + if (Cache.TryGetValue((radius, components), out (IReadOnlyList>, float, IReadOnlyList>) info)) { this.kernelParameters = info.Item1; this.kernelsScale = info.Item2; @@ -177,7 +182,7 @@ from component in this.kernelParameters /// /// The exponential parameter for each complex component /// The angle component for each complex component - private Complex64[] CreateComplex1DKernel(float a, float b) + private DenseMatrix CreateComplex1DKernel(float a, float b) { // Precompute the range values float[] ax = Enumerable.Range(-this.Radius, this.Radius + 1).Select( @@ -188,13 +193,13 @@ private Complex64[] CreateComplex1DKernel(float a, float b) }).ToArray(); // Compute the complex kernels - var kernel = new Complex64[this.kernelSize]; + var kernel = new DenseMatrix(this.kernelSize, 1); for (int i = 0; i < this.kernelSize; i++) { float real = (float)(Math.Exp(-a * ax[i]) * Math.Cos(b * ax[i])), imaginary = (float)(Math.Exp(-a * ax[i]) * Math.Sin(b * ax[i])); - kernel[i] = new Complex64(real, imaginary); + kernel[i, 0] = new Complex64(real, imaginary); } return kernel; @@ -207,31 +212,95 @@ private void NormalizeKernels() { // Calculate the complex weighted sum double total = 0; - foreach ((Complex64[] kernel, IReadOnlyDictionary param) in this.complexKernels.Zip(this.kernelParameters, (k, p) => (k, p))) + foreach ((DenseMatrix kernel, IReadOnlyDictionary param) in this.complexKernels.Zip(this.kernelParameters, (k, p) => (k, p))) { - for (int i = 0; i < kernel.Length; i++) + for (int i = 0; i < kernel.Count; i++) { - for (int j = 0; j < kernel.Length; j++) + for (int j = 0; j < kernel.Count; j++) { total += - (param['A'] * ((kernel[i].Real * kernel[j].Real) - (kernel[i].Imaginary * kernel[j].Imaginary))) + - (param['B'] * ((kernel[i].Real * kernel[j].Imaginary) + (kernel[i].Imaginary * kernel[j].Real))); + (param['A'] * ((kernel[i, 0].Real * kernel[j, 0].Real) - (kernel[i, 0].Imaginary * kernel[j, 0].Imaginary))) + + (param['B'] * ((kernel[i, 0].Real * kernel[j, 0].Imaginary) + (kernel[i, 0].Imaginary * kernel[j, 0].Real))); } } } // Normalize the kernels float scalar = (float)(1f / Math.Sqrt(total)); - foreach (Complex64[] kernel in this.complexKernels) + foreach (DenseMatrix kernel in this.complexKernels) { - for (int i = 0; i < kernel.Length; i++) + for (int i = 0; i < kernel.Count; i++) { - kernel[i] = kernel[i] * scalar; + kernel[i, 0] *= scalar; } } } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) => throw new NotImplementedException(); + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + // Create a 0-filled buffer to use to store the result of the component convolutions + using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size())) + { + // Perform two 1D convolutions for each component in the current insttance + foreach ((DenseMatrix kernel, IReadOnlyDictionary parameters) in this.complexKernels.Zip(this.kernelParameters, (k, p) => (k, p))) + { + using (Buffer2D firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) + { + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); + } + } + + // Copy the processed buffer back to the source image + processing.GetSpan().CopyTo(source.GetPixelSpan()); + } + } + + /// + /// Applies the process to the specified portion of the specified at the specified location + /// and with the specified size. + /// + /// The target values to use to store the results. + /// The source pixels. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// The kernel operator. + /// The + private void ApplyConvolution( + Buffer2D targetValues, + Buffer2D sourcePixels, + Rectangle sourceRectangle, + in DenseMatrix kernel, + Configuration configuration) + { + DenseMatrix matrix = kernel; + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + int maxY = endY - 1; + int maxX = endX - 1; + + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + int width = workingRectangle.Width; + + ParallelHelper.IterateRowsWithTempBuffer( + workingRectangle, + configuration, + (rows, vectorBuffer) => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); + + for (int x = 0; x < width; x++) + { + DenseMatrixUtils.Convolve(in matrix, sourcePixels, targetRowSpan, y, x, maxY, maxX, startX); + } + } + }); + } } } diff --git a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs index c34d50297a..546bdef4b3 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs @@ -1,177 +1,177 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; -using System.Linq; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; -using Xunit; - -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Tests.Drawing -{ - [GroupOutput("Drawing")] - public class SolidFillBlendedShapesTests - { - public static IEnumerable modes = GetAllModeCombinations(); - - private static IEnumerable GetAllModeCombinations() - { - foreach (var composition in Enum.GetValues(typeof(PixelAlphaCompositionMode))) - { - foreach (var blending in Enum.GetValues(typeof(PixelColorBlendingMode))) - { - yield return new object[] { blending, composition }; - } - } - } - - - [Theory] - [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] - public void _1DarkBlueRect_2BlendHotPinkRect( - TestImageProvider provider, - PixelColorBlendingMode blending, - PixelAlphaCompositionMode composition) - where TPixel : struct, IPixel - { - using (Image img = provider.GetImage()) - { - int scaleX = img.Width / 100; - int scaleY = img.Height / 100; - img.Mutate( - x => x.Fill( - Color.DarkBlue, - new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY) - ) - .Fill(new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode=composition }, - Color.HotPink, - new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)) - ); - - VerifyImage(provider, blending, composition, img); - } - } - - [Theory] - [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] - public void _1DarkBlueRect_2BlendHotPinkRect_3BlendTransparentEllipse( - TestImageProvider provider, - PixelColorBlendingMode blending, - PixelAlphaCompositionMode composition) - where TPixel : struct, IPixel - { - using (Image img = provider.GetImage()) - { - int scaleX = img.Width / 100; - int scaleY = img.Height / 100; - img.Mutate( - x => x.Fill( - Color.DarkBlue, - new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); - img.Mutate( - x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, - Color.HotPink, - new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))); - img.Mutate( - x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, - Color.Transparent, - new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) - ); - - VerifyImage(provider, blending, composition, img); - } - } - - [Theory] - [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] - public void _1DarkBlueRect_2BlendHotPinkRect_3BlendSemiTransparentRedEllipse( - TestImageProvider provider, - PixelColorBlendingMode blending, - PixelAlphaCompositionMode composition) - where TPixel : struct, IPixel - { - using (Image img = provider.GetImage()) - { - int scaleX = (img.Width / 100); - int scaleY = (img.Height / 100); - img.Mutate( - x => x.Fill( - Color.DarkBlue, - new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY))); - img.Mutate( - x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, - Color.HotPink, - new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY))); - - var transparentRed = Color.Red.WithAlpha(0.5f); - - img.Mutate( - x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, - transparentRed, - new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) - ); - - VerifyImage(provider, blending, composition, img); ; - } - } - - [Theory] - [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] - public void _1DarkBlueRect_2BlendBlackEllipse( - TestImageProvider provider, - PixelColorBlendingMode blending, - PixelAlphaCompositionMode composition) - where TPixel : struct, IPixel - { - using(Image dstImg = provider.GetImage(), srcImg = provider.GetImage()) - { - int scaleX = (dstImg.Width / 100); - int scaleY = (dstImg.Height / 100); - - dstImg.Mutate( - x => x.Fill( - Color.DarkBlue, - new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); - - srcImg.Mutate( - x => x.Fill( - Color.Black, - new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))); - - dstImg.Mutate( - x => x.DrawImage(srcImg, new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }) - ); - - VerifyImage(provider, blending, composition, dstImg); - } - } - - private static void VerifyImage( - TestImageProvider provider, - PixelColorBlendingMode blending, - PixelAlphaCompositionMode composition, - Image img) - where TPixel : struct, IPixel - { - img.DebugSave( - provider, - new { composition, blending }, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - - var comparer = ImageComparer.TolerantPercentage(0.01f, 3); - img.CompareFirstFrameToReferenceOutput(comparer, - provider, - new { composition, blending }, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. +using System; +using System.Collections.Generic; +using System.Linq; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.Primitives; +using Xunit; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Drawing +{ + [GroupOutput("Drawing")] + public class SolidFillBlendedShapesTests + { + public static IEnumerable modes = GetAllModeCombinations(); + + private static IEnumerable GetAllModeCombinations() + { + foreach (var composition in Enum.GetValues(typeof(PixelAlphaCompositionMode))) + { + foreach (var blending in Enum.GetValues(typeof(PixelColorBlendingMode))) + { + yield return new object[] { blending, composition }; + } + } + } + + + [Theory] + [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] + public void _1DarkBlueRect_2BlendHotPinkRect( + TestImageProvider provider, + PixelColorBlendingMode blending, + PixelAlphaCompositionMode composition) + where TPixel : struct, IPixel + { + using (Image img = provider.GetImage()) + { + int scaleX = img.Width / 100; + int scaleY = img.Height / 100; + img.Mutate( + x => x.Fill( + Color.DarkBlue, + new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY) + ) + .Fill(new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode=composition }, + Color.HotPink, + new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)) + ); + + VerifyImage(provider, blending, composition, img); + } + } + + [Theory] + [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] + public void _1DarkBlueRect_2BlendHotPinkRect_3BlendTransparentEllipse( + TestImageProvider provider, + PixelColorBlendingMode blending, + PixelAlphaCompositionMode composition) + where TPixel : struct, IPixel + { + using (Image img = provider.GetImage()) + { + int scaleX = img.Width / 100; + int scaleY = img.Height / 100; + img.Mutate( + x => x.Fill( + Color.DarkBlue, + new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); + img.Mutate( + x => x.Fill( + new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, + Color.HotPink, + new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))); + img.Mutate( + x => x.Fill( + new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, + Color.Transparent, + new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) + ); + + VerifyImage(provider, blending, composition, img); + } + } + + [Theory] + [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] + public void _1DarkBlueRect_2BlendHotPinkRect_3BlendSemiTransparentRedEllipse( + TestImageProvider provider, + PixelColorBlendingMode blending, + PixelAlphaCompositionMode composition) + where TPixel : struct, IPixel + { + using (Image img = provider.GetImage()) + { + int scaleX = (img.Width / 100); + int scaleY = (img.Height / 100); + img.Mutate( + x => x.Fill( + Color.DarkBlue, + new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY))); + img.Mutate( + x => x.Fill( + new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, + Color.HotPink, + new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY))); + + var transparentRed = Color.Red.WithAlpha(0.5f); + + img.Mutate( + x => x.Fill( + new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, + transparentRed, + new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) + ); + + VerifyImage(provider, blending, composition, img); ; + } + } + + [Theory] + [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] + public void _1DarkBlueRect_2BlendBlackEllipse( + TestImageProvider provider, + PixelColorBlendingMode blending, + PixelAlphaCompositionMode composition) + where TPixel : struct, IPixel + { + using(Image dstImg = provider.GetImage(), srcImg = provider.GetImage()) + { + int scaleX = (dstImg.Width / 100); + int scaleY = (dstImg.Height / 100); + + dstImg.Mutate( + x => x.Fill( + Color.DarkBlue, + new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); + + srcImg.Mutate( + x => x.Fill( + Color.Black, + new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))); + + dstImg.Mutate( + x => x.DrawImage(srcImg, new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }) + ); + + VerifyImage(provider, blending, composition, dstImg); + } + } + + private static void VerifyImage( + TestImageProvider provider, + PixelColorBlendingMode blending, + PixelAlphaCompositionMode composition, + Image img) + where TPixel : struct, IPixel + { + img.DebugSave( + provider, + new { composition, blending }, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + + var comparer = ImageComparer.TolerantPercentage(0.01f, 3); + img.CompareFirstFrameToReferenceOutput(comparer, + provider, + new { composition, blending }, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + } + } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs index c2100c302f..8628d054a0 100644 --- a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs +++ b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs @@ -1,146 +1,146 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; -using System.Linq; -using Moq; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.PixelFormats; -using Xunit; - - -namespace SixLabors.ImageSharp.Tests -{ - public class ImageFormatManagerTests - { - public ImageFormatManager FormatsManagerEmpty { get; } - public ImageFormatManager DefaultFormatsManager { get; } - - public ImageFormatManagerTests() - { - this.DefaultFormatsManager = Configuration.CreateDefaultInstance().ImageFormatsManager; - this.FormatsManagerEmpty = new ImageFormatManager(); - } - - [Fact] - public void IfAutoloadWellKnownFormatsIsTrueAllFormatsAreLoaded() - { - Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); - Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); - Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); - Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); - - Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); - Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); - Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); - Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); - } - - [Fact] - public void AddImageFormatDetectorNullthrows() - { - Assert.Throws(() => - { - this.DefaultFormatsManager.AddImageFormatDetector(null); - }); - } - - [Fact] - public void RegisterNullMimeTypeEncoder() - { - Assert.Throws(() => - { - this.DefaultFormatsManager.SetEncoder(null, new Mock().Object); - }); - Assert.Throws(() => - { - this.DefaultFormatsManager.SetEncoder(BmpFormat.Instance, null); - }); - Assert.Throws(() => - { - this.DefaultFormatsManager.SetEncoder(null, null); - }); - } - - [Fact] - public void RegisterNullSetDecoder() - { - Assert.Throws(() => - { - this.DefaultFormatsManager.SetDecoder(null, new Mock().Object); - }); - Assert.Throws(() => - { - this.DefaultFormatsManager.SetDecoder(BmpFormat.Instance, null); - }); - Assert.Throws(() => - { - this.DefaultFormatsManager.SetDecoder(null, null); - }); - } - - [Fact] - public void RegisterMimeTypeEncoderReplacesLast() - { - IImageEncoder encoder1 = new Mock().Object; - this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); - IImageEncoder found = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); - Assert.Equal(encoder1, found); - - IImageEncoder encoder2 = new Mock().Object; - this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); - IImageEncoder found2 = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); - Assert.Equal(encoder2, found2); - Assert.NotEqual(found, found2); - } - - [Fact] - public void RegisterMimeTypeDecoderReplacesLast() - { - IImageDecoder decoder1 = new Mock().Object; - this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); - IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); - Assert.Equal(decoder1, found); - - IImageDecoder decoder2 = new Mock().Object; - this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); - IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); - Assert.Equal(decoder2, found2); - Assert.NotEqual(found, found2); - } - - [Fact] - public void AddFormatCallsConfig() - { - var provider = new Mock(); - var config = new Configuration(); - config.Configure(provider.Object); - - provider.Verify(x => x.Configure(config)); - } - - [Fact] - public void DetectFormatAllocatesCleanBuffer() - { - byte[] jpegImage; - using (var buffer = new MemoryStream()) - { - using (var image = new Image(100, 100)) - { - image.SaveAsJpeg(buffer); - jpegImage = buffer.ToArray(); - } - } - - byte[] invalidImage = { 1, 2, 3 }; - - Assert.Equal(Image.DetectFormat(jpegImage), JpegFormat.Instance); - Assert.True(Image.DetectFormat(invalidImage) is null); - } - } -} +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Linq; +using Moq; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + + +namespace SixLabors.ImageSharp.Tests +{ + public class ImageFormatManagerTests + { + public ImageFormatManager FormatsManagerEmpty { get; } + public ImageFormatManager DefaultFormatsManager { get; } + + public ImageFormatManagerTests() + { + this.DefaultFormatsManager = Configuration.CreateDefaultInstance().ImageFormatsManager; + this.FormatsManagerEmpty = new ImageFormatManager(); + } + + [Fact] + public void IfAutoloadWellKnownFormatsIsTrueAllFormatsAreLoaded() + { + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + } + + [Fact] + public void AddImageFormatDetectorNullthrows() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.AddImageFormatDetector(null); + }); + } + + [Fact] + public void RegisterNullMimeTypeEncoder() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.SetEncoder(null, new Mock().Object); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetEncoder(BmpFormat.Instance, null); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetEncoder(null, null); + }); + } + + [Fact] + public void RegisterNullSetDecoder() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(null, new Mock().Object); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(BmpFormat.Instance, null); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(null, null); + }); + } + + [Fact] + public void RegisterMimeTypeEncoderReplacesLast() + { + IImageEncoder encoder1 = new Mock().Object; + this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); + IImageEncoder found = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); + Assert.Equal(encoder1, found); + + IImageEncoder encoder2 = new Mock().Object; + this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); + IImageEncoder found2 = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); + Assert.Equal(encoder2, found2); + Assert.NotEqual(found, found2); + } + + [Fact] + public void RegisterMimeTypeDecoderReplacesLast() + { + IImageDecoder decoder1 = new Mock().Object; + this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); + IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); + Assert.Equal(decoder1, found); + + IImageDecoder decoder2 = new Mock().Object; + this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); + IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); + Assert.Equal(decoder2, found2); + Assert.NotEqual(found, found2); + } + + [Fact] + public void AddFormatCallsConfig() + { + var provider = new Mock(); + var config = new Configuration(); + config.Configure(provider.Object); + + provider.Verify(x => x.Configure(config)); + } + + [Fact] + public void DetectFormatAllocatesCleanBuffer() + { + byte[] jpegImage; + using (var buffer = new MemoryStream()) + { + using (var image = new Image(100, 100)) + { + image.SaveAsJpeg(buffer); + jpegImage = buffer.ToArray(); + } + } + + byte[] invalidImage = { 1, 2, 3 }; + + Assert.Equal(Image.DetectFormat(jpegImage), JpegFormat.Instance); + Assert.True(Image.DetectFormat(invalidImage) is null); + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs index 0c7a760818..09a78a6aa1 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs @@ -1,56 +1,56 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders -{ - using SixLabors.ImageSharp.PixelFormats; - using SixLabors.ImageSharp.Processing; - - using Xunit; - - public class PorterDuffCompositorTests - { - // TODO: Add other modes to compare. - public static readonly TheoryData CompositingOperators = - new TheoryData - { - PixelAlphaCompositionMode.Src, - PixelAlphaCompositionMode.SrcAtop, - PixelAlphaCompositionMode.SrcOver, - PixelAlphaCompositionMode.SrcIn, - PixelAlphaCompositionMode.SrcOut, - PixelAlphaCompositionMode.Dest, - PixelAlphaCompositionMode.DestAtop, - PixelAlphaCompositionMode.DestOver, - PixelAlphaCompositionMode.DestIn, - PixelAlphaCompositionMode.DestOut, - PixelAlphaCompositionMode.Clear, - PixelAlphaCompositionMode.Xor - }; - - [Theory] - [WithFile(TestImages.Png.PDDest, nameof(CompositingOperators), PixelTypes.Rgba32)] - public void PorterDuffOutputIsCorrect(TestImageProvider provider, PixelAlphaCompositionMode mode) - { - var srcFile = TestFile.Create(TestImages.Png.PDSrc); - using (Image src = srcFile.CreateRgba32Image()) - using (Image dest = provider.GetImage()) - { - GraphicsOptions options = new GraphicsOptions - { - AlphaCompositionMode = mode - }; - - using (Image res = dest.Clone(x => x.DrawImage(src, options))) - { - string combinedMode = mode.ToString(); - - if (combinedMode != "Src" && combinedMode.StartsWith("Src")) combinedMode = combinedMode.Substring(3); - - res.DebugSave(provider, combinedMode); - res.CompareToReferenceOutput(provider, combinedMode); - } - } - } - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders +{ + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Processing; + + using Xunit; + + public class PorterDuffCompositorTests + { + // TODO: Add other modes to compare. + public static readonly TheoryData CompositingOperators = + new TheoryData + { + PixelAlphaCompositionMode.Src, + PixelAlphaCompositionMode.SrcAtop, + PixelAlphaCompositionMode.SrcOver, + PixelAlphaCompositionMode.SrcIn, + PixelAlphaCompositionMode.SrcOut, + PixelAlphaCompositionMode.Dest, + PixelAlphaCompositionMode.DestAtop, + PixelAlphaCompositionMode.DestOver, + PixelAlphaCompositionMode.DestIn, + PixelAlphaCompositionMode.DestOut, + PixelAlphaCompositionMode.Clear, + PixelAlphaCompositionMode.Xor + }; + + [Theory] + [WithFile(TestImages.Png.PDDest, nameof(CompositingOperators), PixelTypes.Rgba32)] + public void PorterDuffOutputIsCorrect(TestImageProvider provider, PixelAlphaCompositionMode mode) + { + var srcFile = TestFile.Create(TestImages.Png.PDSrc); + using (Image src = srcFile.CreateRgba32Image()) + using (Image dest = provider.GetImage()) + { + GraphicsOptions options = new GraphicsOptions + { + AlphaCompositionMode = mode + }; + + using (Image res = dest.Clone(x => x.DrawImage(src, options))) + { + string combinedMode = mode.ToString(); + + if (combinedMode != "Src" && combinedMode.StartsWith("Src")) combinedMode = combinedMode.Substring(3); + + res.DebugSave(provider, combinedMode); + res.CompareToReferenceOutput(provider, combinedMode); + } + } + } + } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs index 3c562057a8..a044ebae9b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { public class PixelBlenderTests - { + { public static TheoryData BlenderMappings = new TheoryData() { { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcOver), PixelColorBlendingMode.Normal }, @@ -43,62 +43,62 @@ public void ReturnsCorrectBlender(TestPixel pixel, Type type, Pi { PixelBlender blender = PixelOperations.Instance.GetPixelBlender(mode, PixelAlphaCompositionMode.SrcOver); Assert.IsType(type, blender); - } - - public static TheoryData ColorBlendingExpectedResults = new TheoryData() - { - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Normal, Rgba32.MidnightBlue }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Screen, new Rgba32(0xFFEEE7FF) }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.HardLight, new Rgba32(0xFFC62D32) }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Overlay, new Rgba32(0xFFDDCEFF) }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Darken, new Rgba32(0xFF701919) }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Lighten, new Rgba32(0xFFE1E4FF) }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Add, new Rgba32(0xFFFFFDFF) }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Subtract, new Rgba32(0xFF71CBE6) }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Multiply, new Rgba32(0xFF631619) }, - - }; - + } + + public static TheoryData ColorBlendingExpectedResults = new TheoryData() + { + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Normal, Rgba32.MidnightBlue }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Screen, new Rgba32(0xFFEEE7FF) }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.HardLight, new Rgba32(0xFFC62D32) }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Overlay, new Rgba32(0xFFDDCEFF) }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Darken, new Rgba32(0xFF701919) }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Lighten, new Rgba32(0xFFE1E4FF) }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Add, new Rgba32(0xFFFFFDFF) }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Subtract, new Rgba32(0xFF71CBE6) }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Multiply, new Rgba32(0xFF631619) }, + + }; + [Theory] - [MemberData(nameof(ColorBlendingExpectedResults))] - public void TestColorBlendingModes(Rgba32 backdrop, Rgba32 source, float opacity, PixelColorBlendingMode mode, Rgba32 expectedResult) - { - PixelBlender blender = PixelOperations.Instance.GetPixelBlender(mode, PixelAlphaCompositionMode.SrcOver); - - Rgba32 actualResult = blender.Blend(backdrop, source, opacity); - - // var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults - - Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4()); - } - - public static TheoryData AlphaCompositionExpectedResults = new TheoryData() - { - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Clear, new Rgba32(0) }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Xor, new Rgba32(0) }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Dest, Rgba32.MistyRose }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestAtop, Rgba32.MistyRose }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestIn, Rgba32.MistyRose }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestOut, new Rgba32(0) }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestOver, Rgba32.MistyRose }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Src, Rgba32.MidnightBlue }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcAtop, Rgba32.MidnightBlue }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcIn, Rgba32.MidnightBlue }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcOut, new Rgba32(0) }, - { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcOver, Rgba32.MidnightBlue }, - }; - + [MemberData(nameof(ColorBlendingExpectedResults))] + public void TestColorBlendingModes(Rgba32 backdrop, Rgba32 source, float opacity, PixelColorBlendingMode mode, Rgba32 expectedResult) + { + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(mode, PixelAlphaCompositionMode.SrcOver); + + Rgba32 actualResult = blender.Blend(backdrop, source, opacity); + + // var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults + + Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4()); + } + + public static TheoryData AlphaCompositionExpectedResults = new TheoryData() + { + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Clear, new Rgba32(0) }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Xor, new Rgba32(0) }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Dest, Rgba32.MistyRose }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestAtop, Rgba32.MistyRose }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestIn, Rgba32.MistyRose }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestOut, new Rgba32(0) }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestOver, Rgba32.MistyRose }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Src, Rgba32.MidnightBlue }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcAtop, Rgba32.MidnightBlue }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcIn, Rgba32.MidnightBlue }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcOut, new Rgba32(0) }, + { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcOver, Rgba32.MidnightBlue }, + }; + [Theory] - [MemberData(nameof(AlphaCompositionExpectedResults))] - public void TestAlphaCompositionModes(Rgba32 backdrop, Rgba32 source, float opacity, PixelAlphaCompositionMode mode, Rgba32 expectedResult) - { - PixelBlender blender = PixelOperations.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, mode); - - Rgba32 actualResult = blender.Blend(backdrop, source, opacity); - - // var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults - - Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4()); + [MemberData(nameof(AlphaCompositionExpectedResults))] + public void TestAlphaCompositionModes(Rgba32 backdrop, Rgba32 source, float opacity, PixelAlphaCompositionMode mode, Rgba32 expectedResult) + { + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, mode); + + Rgba32 actualResult = blender.Blend(backdrop, source, opacity); + + // var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults + + Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4()); } } } From ceebb7bcc0c4ab70060642636a4d30e78af14587 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 21 Feb 2019 16:12:07 +0100 Subject: [PATCH 14/82] Added second bokeh convolution pass (WIP) --- .../Convolution/BokehBlurProcessor.cs | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 67f0c39c58..2f9be111d4 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -242,13 +242,16 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source // Create a 0-filled buffer to use to store the result of the component convolutions using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size())) { - // Perform two 1D convolutions for each component in the current insttance + // Perform two 1D convolutions for each component in the current instance foreach ((DenseMatrix kernel, IReadOnlyDictionary parameters) in this.complexKernels.Zip(this.kernelParameters, (k, p) => (k, p))) { - using (Buffer2D firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) + using (Buffer2D + firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size()), + partialValues = configuration.MemoryAllocator.Allocate2D(source.Size())) { var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); + this.ApplyConvolution(partialValues, firstPassValues, interest, kernel.Reshape(1, kernel.Count), configuration); } } @@ -302,5 +305,51 @@ private void ApplyConvolution( } }); } + + /// + /// Applies the process to the specified portion of the specified at the specified location + /// and with the specified size. + /// + /// The target values to use to store the results. + /// The source complex values. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// The kernel operator. + /// The + private void ApplyConvolution( + Buffer2D targetValues, + Buffer2D sourceValues, + Rectangle sourceRectangle, + in DenseMatrix kernel, + Configuration configuration) + { + DenseMatrix matrix = kernel; + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + int maxY = endY - 1; + int maxX = endX - 1; + + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + int width = workingRectangle.Width; + + ParallelHelper.IterateRowsWithTempBuffer( + workingRectangle, + configuration, + (rows, vectorBuffer) => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); + + for (int x = 0; x < width; x++) + { + DenseMatrixUtils.Convolve(in matrix, sourceValues, targetRowSpan, y, x, maxY, maxX, startX); + } + } + }); + } } } From 732d229e470513ad28d4ca691d2337759fd69148 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 21 Feb 2019 16:32:54 +0100 Subject: [PATCH 15/82] Added image sum pass to the bokeh processor (WIP) --- .../Convolution/BokehBlurProcessor.cs | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 2f9be111d4..76b306bff1 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -240,23 +240,45 @@ private void NormalizeKernels() protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { // Create a 0-filled buffer to use to store the result of the component convolutions - using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size())) + using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size())) { + Span processingSpan = processing.Span; + // Perform two 1D convolutions for each component in the current instance foreach ((DenseMatrix kernel, IReadOnlyDictionary parameters) in this.complexKernels.Zip(this.kernelParameters, (k, p) => (k, p))) { - using (Buffer2D - firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size()), - partialValues = configuration.MemoryAllocator.Allocate2D(source.Size())) + using (Buffer2D partialValues = configuration.MemoryAllocator.Allocate2D(source.Size())) { - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); - this.ApplyConvolution(partialValues, firstPassValues, interest, kernel.Reshape(1, kernel.Count), configuration); + // Compute the resulting complex buffer for the current component + using (Buffer2D firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) + { + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); + this.ApplyConvolution(partialValues, firstPassValues, interest, kernel.Reshape(1, kernel.Count), configuration); + } + + // Add the results of the convolution with the current kernel + Span partialSpan = partialValues.Span; + for (int i = 0; i < processingSpan.Length; i++) + { + var vector = new Vector4( + partialSpan[i].X.WeightedSum(parameters['A'], parameters['B']), + partialSpan[i].Y.WeightedSum(parameters['A'], parameters['B']), + partialSpan[i].Z.WeightedSum(parameters['A'], parameters['B']), + partialSpan[i].W.WeightedSum(parameters['A'], parameters['B'])); + processingSpan[i] += vector; + } } } // Copy the processed buffer back to the source image - processing.GetSpan().CopyTo(source.GetPixelSpan()); + Span sourceSpan = source.GetPixelSpan(); + for (int i = 0; i < sourceSpan.Length; i++) + { + TPixel pixel = default; + pixel.FromVector4(processingSpan[i]); + sourceSpan[i] = pixel; + } } } From 5fe13ea76a8e221a2ad2970a89ffe8726437d0ae Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 21 Feb 2019 16:53:48 +0100 Subject: [PATCH 16/82] Minor bug fixes (WIP) --- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 76b306bff1..2a4ba0a263 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -185,7 +185,7 @@ from component in this.kernelParameters private DenseMatrix CreateComplex1DKernel(float a, float b) { // Precompute the range values - float[] ax = Enumerable.Range(-this.Radius, this.Radius + 1).Select( + float[] ax = Enumerable.Range(-this.Radius, (this.Radius * 2) + 1).Select( i => { float value = i * this.kernelsScale * (1f / this.Radius); @@ -193,7 +193,7 @@ private DenseMatrix CreateComplex1DKernel(float a, float b) }).ToArray(); // Compute the complex kernels - var kernel = new DenseMatrix(this.kernelSize, 1); + var kernel = new DenseMatrix(1, this.kernelSize); for (int i = 0; i < this.kernelSize; i++) { float @@ -254,7 +254,7 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source { var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); - this.ApplyConvolution(partialValues, firstPassValues, interest, kernel.Reshape(1, kernel.Count), configuration); + this.ApplyConvolution(partialValues, firstPassValues, interest, kernel.Reshape(kernel.Count, 1), configuration); } // Add the results of the convolution with the current kernel From df8c78653439e6d19f4b755de6be044fcf5e7078 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 21 Feb 2019 17:18:52 +0100 Subject: [PATCH 17/82] Switched to Vector4 processing in the bokeh computation --- src/ImageSharp/Primitives/Complex64.cs | 35 +++++++++--------- src/ImageSharp/Primitives/ComplexVector4.cs | 36 ++++++++++++++++--- .../Convolution/BokehBlurProcessor.cs | 7 +--- 3 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/ImageSharp/Primitives/Complex64.cs b/src/ImageSharp/Primitives/Complex64.cs index 890ff6228e..fdcc8bf83a 100644 --- a/src/ImageSharp/Primitives/Complex64.cs +++ b/src/ImageSharp/Primitives/Complex64.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Primitives @@ -45,31 +46,31 @@ public Complex64(float real, float imaginary) public static Complex64 operator *(Complex64 value, float scalar) => new Complex64(value.Real * scalar, value.Imaginary * scalar); /// - /// Performs the addition operation between two intances. + /// Performs the multiplication operation between a intance and a . /// - /// The first value to sum. - /// The second value to sum. + /// The value to multiply. + /// The instance to use to multiply the value. /// The result [MethodImpl(InliningOptions.ShortMethod)] - public static Complex64 operator +(Complex64 left, Complex64 right) => new Complex64(left.Real + right.Real, left.Imaginary + right.Imaginary); + public static ComplexVector4 operator *(Complex64 value, Vector4 vector) + { + return new ComplexVector4 { Real = vector * value.Real, Imaginary = vector * value.Imaginary }; + } /// - /// Performs the multiplication operation between two intances. + /// Performs the multiplication operation between a intance and a . /// - /// The first value to multiply. - /// The second value to multiply. + /// The value to multiply. + /// The instance to use to multiply the value. /// The result [MethodImpl(InliningOptions.ShortMethod)] - public static Complex64 operator *(Complex64 left, Complex64 right) => new Complex64((left.Real * right.Real) - (left.Imaginary * right.Imaginary), (left.Real * right.Imaginary) + (left.Imaginary * right.Real)); - - /// - /// Performs a weighted sum on the current instance according to the given parameters - /// - /// The 'a' parameter, for the real component - /// The 'b' parameter, for the imaginary component - /// The resulting value - [MethodImpl(InliningOptions.ShortMethod)] - public float WeightedSum(float a, float b) => (this.Real * a) + (this.Imaginary * b); + public static ComplexVector4 operator *(Complex64 value, ComplexVector4 vector) + { + Vector4 + real = (value.Real * vector.Real) - (value.Imaginary * vector.Imaginary), + imaginary = (value.Real * vector.Imaginary) + (value.Imaginary * vector.Real); + return new ComplexVector4 { Real = real, Imaginary = imaginary }; + } /// public bool Equals(Complex64 other) diff --git a/src/ImageSharp/Primitives/ComplexVector4.cs b/src/ImageSharp/Primitives/ComplexVector4.cs index ead7234f47..4a4e0d6152 100644 --- a/src/ImageSharp/Primitives/ComplexVector4.cs +++ b/src/ImageSharp/Primitives/ComplexVector4.cs @@ -1,6 +1,9 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System.Numerics; +using System.Runtime.CompilerServices; + namespace SixLabors.ImageSharp.Primitives { /// @@ -8,9 +11,34 @@ namespace SixLabors.ImageSharp.Primitives /// internal struct ComplexVector4 { - public Complex64 X; - public Complex64 Y; - public Complex64 Z; - public Complex64 W; + /// + /// The real part of the complex vector + /// + public Vector4 Real; + + /// + /// The imaginary part of the complex number + /// + public Vector4 Imaginary; + + /// + /// Sums the values in the input to the current instance + /// + /// The input to sum + [MethodImpl(InliningOptions.ShortMethod)] + public void Sum(in ComplexVector4 value) + { + this.Real += value.Real; + this.Imaginary += value.Imaginary; + } + + /// + /// Performs a weighted sum on the current instance according to the given parameters + /// + /// The 'a' parameter, for the real component + /// The 'b' parameter, for the imaginary component + /// The resulting value + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 WeightedSum(float a, float b) => (this.Real * a) + (this.Imaginary * b); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 2a4ba0a263..69decac593 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -261,12 +261,7 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source Span partialSpan = partialValues.Span; for (int i = 0; i < processingSpan.Length; i++) { - var vector = new Vector4( - partialSpan[i].X.WeightedSum(parameters['A'], parameters['B']), - partialSpan[i].Y.WeightedSum(parameters['A'], parameters['B']), - partialSpan[i].Z.WeightedSum(parameters['A'], parameters['B']), - partialSpan[i].W.WeightedSum(parameters['A'], parameters['B'])); - processingSpan[i] += vector; + processingSpan[i] += partialSpan[i].WeightedSum(parameters['A'], parameters['B']); } } } From 9bd1c8744848e34dc1f46e26ccd601f7d220cee4 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 21 Feb 2019 17:24:56 +0100 Subject: [PATCH 18/82] Minor tweaks --- src/ImageSharp/Primitives/Complex64.cs | 2 +- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Primitives/Complex64.cs b/src/ImageSharp/Primitives/Complex64.cs index fdcc8bf83a..e3d4857430 100644 --- a/src/ImageSharp/Primitives/Complex64.cs +++ b/src/ImageSharp/Primitives/Complex64.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Primitives /// Represents a complex number, where the real and imaginary parts are stored as values. /// /// - /// This is a more efficient version of the type. + /// This is a more efficient version of the type. /// internal readonly struct Complex64 : IEquatable { diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 69decac593..386f17023e 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -54,7 +54,7 @@ internal class BokehBlurProcessor : ImageProcessor new Dictionary<(int, int), (IReadOnlyList>, float, IReadOnlyList>)>(); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. @@ -324,7 +324,7 @@ private void ApplyConvolution( } /// - /// Applies the process to the specified portion of the specified at the specified location + /// Applies the process to the specified portion of the specified buffer at the specified location /// and with the specified size. /// /// The target values to use to store the results. From 15d0a98d7632a7a4e532869ad20b78163dc59bc4 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Feb 2019 16:11:24 +0100 Subject: [PATCH 19/82] Added Unit test for the bokeh kernel components --- src/ImageSharp/Primitives/Complex64.cs | 3 + .../Convolution/BokehBlurProcessor.cs | 26 +++---- .../Processors/Convolution/BokehBlurTest.cs | 67 +++++++++++++++++++ 3 files changed, 83 insertions(+), 13 deletions(-) create mode 100644 tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs diff --git a/src/ImageSharp/Primitives/Complex64.cs b/src/ImageSharp/Primitives/Complex64.cs index e3d4857430..9ce6f4a4d7 100644 --- a/src/ImageSharp/Primitives/Complex64.cs +++ b/src/ImageSharp/Primitives/Complex64.cs @@ -89,5 +89,8 @@ public override int GetHashCode() return (this.Real.GetHashCode() * 397) ^ this.Imaginary.GetHashCode(); } } + + /// + public override string ToString() => $"{this.Real}+{this.Imaginary}j"; } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 386f17023e..290148d680 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -42,11 +42,6 @@ internal class BokehBlurProcessor : ImageProcessor /// private readonly float kernelsScale; - /// - /// The complex kernels to use to apply the blur for the current instance - /// - private readonly IReadOnlyList> complexKernels; - /// /// The mapping of initialized complex kernels and parameters, to speed up the initialization of new instances /// @@ -73,19 +68,19 @@ public BokehBlurProcessor(int radius = 32, int components = 2) { this.kernelParameters = info.Item1; this.kernelsScale = info.Item2; - this.complexKernels = info.Item3; + this.Kernels = info.Item3; } else { // Initialize the complex kernels and parameters with the current arguments (this.kernelParameters, this.kernelsScale) = this.GetParameters(); - this.complexKernels = ( - from component in this.kernelParameters - select this.CreateComplex1DKernel(component['a'], component['b'])).ToArray(); + this.Kernels = ( + from component in this.kernelParameters + select this.CreateComplex1DKernel(component['a'], component['b'])).ToArray(); this.NormalizeKernels(); // Store them in the cache for future use - Cache.Add((radius, components), (this.kernelParameters, this.kernelsScale, this.complexKernels)); + Cache.Add((radius, components), (this.kernelParameters, this.kernelsScale, this.Kernels)); } } @@ -94,6 +89,11 @@ from component in this.kernelParameters /// public int Radius { get; } + /// + /// Gets the complex kernels to use to apply the blur for the current instance + /// + public IReadOnlyList> Kernels { get; } + /// /// Gets the kernel scales to adjust the component values in each kernel /// @@ -212,7 +212,7 @@ private void NormalizeKernels() { // Calculate the complex weighted sum double total = 0; - foreach ((DenseMatrix kernel, IReadOnlyDictionary param) in this.complexKernels.Zip(this.kernelParameters, (k, p) => (k, p))) + foreach ((DenseMatrix kernel, IReadOnlyDictionary param) in this.Kernels.Zip(this.kernelParameters, (k, p) => (k, p))) { for (int i = 0; i < kernel.Count; i++) { @@ -227,7 +227,7 @@ private void NormalizeKernels() // Normalize the kernels float scalar = (float)(1f / Math.Sqrt(total)); - foreach (DenseMatrix kernel in this.complexKernels) + foreach (DenseMatrix kernel in this.Kernels) { for (int i = 0; i < kernel.Count; i++) { @@ -245,7 +245,7 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source Span processingSpan = processing.Span; // Perform two 1D convolutions for each component in the current instance - foreach ((DenseMatrix kernel, IReadOnlyDictionary parameters) in this.complexKernels.Zip(this.kernelParameters, (k, p) => (k, p))) + foreach ((DenseMatrix kernel, IReadOnlyDictionary parameters) in this.Kernels.Zip(this.kernelParameters, (k, p) => (k, p))) { using (Buffer2D partialValues = configuration.MemoryAllocator.Allocate2D(source.Size())) { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs new file mode 100644 index 0000000000..ed2a6ab279 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -0,0 +1,67 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.ImageSharp.Processing.Processors.Convolution; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution +{ + public class BokehBlurTest : FileTestBase + { + private static readonly string Components10x2 =@" + [[ 0.00451261+0.0165137j 0.02161237-0.00299122j 0.00387479-0.02682816j + -0.02752798-0.01788438j -0.03553877+0.0154543j -0.01428268+0.04224722j + 0.01747482+0.04687464j 0.04243676+0.03451751j 0.05564306+0.01742537j + 0.06040984+0.00459225j 0.06136251+0.0j 0.06040984+0.00459225j + 0.05564306+0.01742537j 0.04243676+0.03451751j 0.01747482+0.04687464j + -0.01428268+0.04224722j -0.03553877+0.0154543j -0.02752798-0.01788438j + 0.00387479-0.02682816j 0.02161237-0.00299122j 0.00451261+0.0165137j ]] + [[-0.00227282+0.002851j -0.00152245+0.00604545j 0.00135338+0.00998296j + 0.00698622+0.01370844j 0.0153483+0.01605112j 0.02565295+0.01611732j + 0.03656958+0.01372368j 0.04662725+0.00954624j 0.05458942+0.00491277j + 0.05963937+0.00133843j 0.06136251+0.0j 0.05963937+0.00133843j + 0.05458942+0.00491277j 0.04662725+0.00954624j 0.03656958+0.01372368j + 0.02565295+0.01611732j 0.0153483+0.01605112j 0.00698622+0.01370844j + 0.00135338+0.00998296j -0.00152245+0.00604545j -0.00227282+0.002851j ]]"; + + [Fact] + public void VerifyComplexComponents() + { + // Get the saved components + var components = new List(); + foreach (Match match in Regex.Matches(Components10x2, @"\[\[(.*?)\]\]", RegexOptions.Singleline)) + { + string[] values = match.Groups[1].Value.Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + Complex64[] component = values.Select( + value => + { + Match pair = Regex.Match(value.Replace('.', ','), @"([+-]?\d+,\d+)([+-]?\d+,\d+)j"); + return new Complex64(float.Parse(pair.Groups[1].Value), float.Parse(pair.Groups[2].Value)); + }).ToArray(); + components.Add(component); + } + + // Make sure the kernel components are the same + var processor = new BokehBlurProcessor(10); + Assert.Equal(components.Count, processor.Kernels.Count); + foreach ((Complex64[] a, DenseMatrix b) in components.Zip(processor.Kernels, (a, b) => (a, b))) + { + Span spanA = a.AsSpan(), spanB = b.Span; + Assert.Equal(spanA.Length, spanB.Length); + for (int i = 0; i < spanA.Length; i++) + { + Assert.True(Math.Abs(Math.Abs(spanA[i].Real) - Math.Abs(spanB[i].Real)) < 0.00000001f); + Assert.True(Math.Abs(Math.Abs(spanA[i].Imaginary) - Math.Abs(spanB[i].Imaginary)) < 0.00000001f); + } + } + } + } +} From 6fc9c5011ecd6d654a9c6869bb7b568f9e94016e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Feb 2019 16:19:36 +0100 Subject: [PATCH 20/82] Minor performance improvements --- .../Convolution/BokehBlurProcessor.cs | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 290148d680..73eff1d791 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -33,9 +33,9 @@ internal class BokehBlurProcessor : ImageProcessor private readonly int componentsCount; /// - /// The kernel components to use for the current instance + /// The kernel components to use for the current instance (a: X, b: Y, A: Z, B: W) /// - private readonly IReadOnlyList> kernelParameters; + private readonly IReadOnlyList kernelParameters; /// /// The scaling factor for kernel values @@ -45,8 +45,8 @@ internal class BokehBlurProcessor : ImageProcessor /// /// The mapping of initialized complex kernels and parameters, to speed up the initialization of new instances /// - private static readonly Dictionary<(int, int), (IReadOnlyList>, float, IReadOnlyList>)> Cache = - new Dictionary<(int, int), (IReadOnlyList>, float, IReadOnlyList>)>(); + private static readonly Dictionary<(int, int), (IReadOnlyList, float, IReadOnlyList>)> Cache = + new Dictionary<(int, int), (IReadOnlyList, float, IReadOnlyList>)>(); /// /// Initializes a new instance of the class. @@ -64,7 +64,7 @@ public BokehBlurProcessor(int radius = 32, int components = 2) this.componentsCount = components; // Reuse the initialized values from the cache, if possible - if (Cache.TryGetValue((radius, components), out (IReadOnlyList>, float, IReadOnlyList>) info)) + if (Cache.TryGetValue((radius, components), out (IReadOnlyList, float, IReadOnlyList>) info)) { this.kernelParameters = info.Item1; this.kernelsScale = info.Item2; @@ -75,8 +75,8 @@ public BokehBlurProcessor(int radius = 32, int components = 2) // Initialize the complex kernels and parameters with the current arguments (this.kernelParameters, this.kernelsScale) = this.GetParameters(); this.Kernels = ( - from component in this.kernelParameters - select this.CreateComplex1DKernel(component['a'], component['b'])).ToArray(); + from parameters in this.kernelParameters + select this.CreateComplex1DKernel(parameters.X, parameters.Y)).ToArray(); this.NormalizeKernels(); // Store them in the cache for future use @@ -156,21 +156,19 @@ from component in this.kernelParameters /// /// Gets the kernel parameters and scaling factor for the current count value in the current instance /// - private (IReadOnlyList> Parameters, float Scale) GetParameters() + private (IReadOnlyList Parameters, float Scale) GetParameters() { // Prepare the kernel components int index = Math.Max(0, Math.Min(this.componentsCount - 1, KernelComponents.Count)); float[,] parameters = KernelComponents[index]; - var mapping = new IReadOnlyDictionary[parameters.GetLength(0)]; + var mapping = new Vector4[parameters.GetLength(0)]; for (int i = 0; i < parameters.GetLength(0); i++) { - mapping[i] = new Dictionary - { - ['a'] = parameters[i, 0], - ['b'] = parameters[i, 1], - ['A'] = parameters[i, 2], - ['B'] = parameters[i, 3] - }; + mapping[i] = new Vector4( + parameters[i, 0], + parameters[i, 1], + parameters[i, 2], + parameters[i, 3]); } // Return the components and the adjustment scale @@ -212,15 +210,15 @@ private void NormalizeKernels() { // Calculate the complex weighted sum double total = 0; - foreach ((DenseMatrix kernel, IReadOnlyDictionary param) in this.Kernels.Zip(this.kernelParameters, (k, p) => (k, p))) + foreach ((DenseMatrix kernel, Vector4 param) in this.Kernels.Zip(this.kernelParameters, (k, p) => (k, p))) { for (int i = 0; i < kernel.Count; i++) { for (int j = 0; j < kernel.Count; j++) { total += - (param['A'] * ((kernel[i, 0].Real * kernel[j, 0].Real) - (kernel[i, 0].Imaginary * kernel[j, 0].Imaginary))) + - (param['B'] * ((kernel[i, 0].Real * kernel[j, 0].Imaginary) + (kernel[i, 0].Imaginary * kernel[j, 0].Real))); + (param.Z * ((kernel[i, 0].Real * kernel[j, 0].Real) - (kernel[i, 0].Imaginary * kernel[j, 0].Imaginary))) + + (param.W * ((kernel[i, 0].Real * kernel[j, 0].Imaginary) + (kernel[i, 0].Imaginary * kernel[j, 0].Real))); } } } @@ -245,7 +243,7 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source Span processingSpan = processing.Span; // Perform two 1D convolutions for each component in the current instance - foreach ((DenseMatrix kernel, IReadOnlyDictionary parameters) in this.Kernels.Zip(this.kernelParameters, (k, p) => (k, p))) + foreach ((DenseMatrix kernel, Vector4 parameters) in this.Kernels.Zip(this.kernelParameters, (k, p) => (k, p))) { using (Buffer2D partialValues = configuration.MemoryAllocator.Allocate2D(source.Size())) { @@ -261,7 +259,7 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source Span partialSpan = partialValues.Span; for (int i = 0; i < processingSpan.Length; i++) { - processingSpan[i] += partialSpan[i].WeightedSum(parameters['A'], parameters['B']); + processingSpan[i] += partialSpan[i].WeightedSum(parameters.Z, parameters.W); } } } From 5985904590f0839cc25ba9ba2cba9918221817d1 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Feb 2019 17:18:44 +0100 Subject: [PATCH 21/82] Minor code refactoring, added gamma parameter (WIP) --- .../Processing/BokehBlurExtensions.cs | 10 +-- .../Convolution/BokehBlurProcessor.cs | 69 ++++++++++++------- 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/src/ImageSharp/Processing/BokehBlurExtensions.cs b/src/ImageSharp/Processing/BokehBlurExtensions.cs index 9d7dd65f43..ea251794a7 100644 --- a/src/ImageSharp/Processing/BokehBlurExtensions.cs +++ b/src/ImageSharp/Processing/BokehBlurExtensions.cs @@ -29,10 +29,11 @@ public static IImageProcessingContext BokehBlur(this IImageProce /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. + /// The gamma highlight factor to use to emphasize bright spots in the source image /// The . - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components) + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma) where TPixel : struct, IPixel - => source.ApplyProcessor(new BokehBlurProcessor(radius, components)); + => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma)); /// /// Applies a bokeh blur to the image. @@ -41,12 +42,13 @@ public static IImageProcessingContext BokehBlur(this IImageProce /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. + /// The gamma highlight factor to use to emphasize bright spots in the source image /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, Rectangle rectangle) + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle) where TPixel : struct, IPixel - => source.ApplyProcessor(new BokehBlurProcessor(radius, components), rectangle); + => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), rectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 73eff1d791..91b2e4832c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -57,12 +57,18 @@ internal class BokehBlurProcessor : ImageProcessor /// /// The number of components to use to approximate the original 2D bokeh blur convolution kernel. /// - public BokehBlurProcessor(int radius = 32, int components = 2) + /// + /// The gamma highlight factor to use to further process the image. + /// + public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) { this.Radius = radius; this.kernelSize = (radius * 2) + 1; this.componentsCount = components; + SixLabors.Guard.MustBeGreaterThanOrEqualTo(gamma, 1, nameof(gamma)); + this.Gamma = gamma; + // Reuse the initialized values from the cache, if possible if (Cache.TryGetValue((radius, components), out (IReadOnlyList, float, IReadOnlyList>) info)) { @@ -94,6 +100,11 @@ from parameters in this.kernelParameters /// public IReadOnlyList> Kernels { get; } + /// + /// Gets the gamma highlight factor to use when applying the effect + /// + public float Gamma { get; } + /// /// Gets the kernel scales to adjust the component values in each kernel /// @@ -240,29 +251,9 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source // Create a 0-filled buffer to use to store the result of the component convolutions using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size())) { + // Apply the complex 1D convolutions Span processingSpan = processing.Span; - - // Perform two 1D convolutions for each component in the current instance - foreach ((DenseMatrix kernel, Vector4 parameters) in this.Kernels.Zip(this.kernelParameters, (k, p) => (k, p))) - { - using (Buffer2D partialValues = configuration.MemoryAllocator.Allocate2D(source.Size())) - { - // Compute the resulting complex buffer for the current component - using (Buffer2D firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) - { - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); - this.ApplyConvolution(partialValues, firstPassValues, interest, kernel.Reshape(kernel.Count, 1), configuration); - } - - // Add the results of the convolution with the current kernel - Span partialSpan = partialValues.Span; - for (int i = 0; i < processingSpan.Length; i++) - { - processingSpan[i] += partialSpan[i].WeightedSum(parameters.Z, parameters.W); - } - } - } + this.OnFrameApplyCore(source, processingSpan, sourceRectangle, configuration); // Copy the processed buffer back to the source image Span sourceSpan = source.GetPixelSpan(); @@ -275,6 +266,38 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source } } + /// + /// Applies the actual bokeh blur effect on the target image frame + /// + /// The source image. Cannot be null. + /// The target with the buffer to write to. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + private void OnFrameApplyCore(ImageFrame source, Span processingSpan, Rectangle sourceRectangle, Configuration configuration) + { + // Perform two 1D convolutions for each component in the current instance + foreach ((DenseMatrix kernel, Vector4 parameters) in this.Kernels.Zip(this.kernelParameters, (k, p) => (k, p))) + { + using (Buffer2D partialValues = configuration.MemoryAllocator.Allocate2D(source.Size())) + { + // Compute the resulting complex buffer for the current component + using (Buffer2D firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) + { + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); + this.ApplyConvolution(partialValues, firstPassValues, interest, kernel.Reshape(kernel.Count, 1), configuration); + } + + // Add the results of the convolution with the current kernel + Span partialSpan = partialValues.Span; + for (int i = 0; i < processingSpan.Length; i++) + { + processingSpan[i] += partialSpan[i].WeightedSum(parameters.Z, parameters.W); + } + } + } + } + /// /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. From 3b7ad24fa63d7089e921180795e0a15ff33ca363 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Feb 2019 17:30:18 +0100 Subject: [PATCH 22/82] Removed unused temp buffers in the bokeh processing --- .../Processors/Convolution/BokehBlurProcessor.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 91b2e4832c..72eda92861 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -327,10 +327,10 @@ private void ApplyConvolution( var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); int width = workingRectangle.Width; - ParallelHelper.IterateRowsWithTempBuffer( + ParallelHelper.IterateRows( workingRectangle, configuration, - (rows, vectorBuffer) => + rows => { for (int y = rows.Min; y < rows.Max; y++) { @@ -373,10 +373,10 @@ private void ApplyConvolution( var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); int width = workingRectangle.Width; - ParallelHelper.IterateRowsWithTempBuffer( + ParallelHelper.IterateRows( workingRectangle, configuration, - (rows, vectorBuffer) => + rows => { for (int y = rows.Min; y < rows.Max; y++) { From c6549651fe5dd5fe0a20b875ed407e44db52f733 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Feb 2019 18:08:39 +0100 Subject: [PATCH 23/82] Gamma highlight processing implemented --- .../Convolution/BokehBlurProcessor.cs | 109 ++++++++++++++++-- 1 file changed, 100 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 72eda92861..88b549a8d7 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Numerics; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; @@ -248,6 +247,9 @@ private void NormalizeKernels() /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { + // Preliminary gamma highlight pass + this.ApplyGammaExposure(source.PixelBuffer, sourceRectangle, configuration); + // Create a 0-filled buffer to use to store the result of the component convolutions using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size())) { @@ -255,14 +257,8 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source Span processingSpan = processing.Span; this.OnFrameApplyCore(source, processingSpan, sourceRectangle, configuration); - // Copy the processed buffer back to the source image - Span sourceSpan = source.GetPixelSpan(); - for (int i = 0; i < sourceSpan.Length; i++) - { - TPixel pixel = default; - pixel.FromVector4(processingSpan[i]); - sourceSpan[i] = pixel; - } + // Apply the inverse gamma exposure pass, and write the final pixel data + this.ApplyInverseGammaExposure(source.PixelBuffer, processing, sourceRectangle, configuration); } } @@ -389,5 +385,100 @@ private void ApplyConvolution( } }); } + + /// + /// Applies the gamma correction/highlight to the input pixel buffer. + /// + /// The target pixel buffer to adjust. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// The + private void ApplyGammaExposure( + Buffer2D targetPixels, + Rectangle sourceRectangle, + Configuration configuration) + { + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + int width = workingRectangle.Width; + + ParallelHelper.IterateRowsWithTempBuffer( + workingRectangle, + configuration, + (rows, vectorBuffer) => + { + Span vectorSpan = vectorBuffer.Span; + int length = vectorSpan.Length; + + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan); + + for (int x = 0; x < width; x++) + { + ref Vector4 vector = ref vectorSpan[x]; + vector.X = (float)Math.Pow(vector.X, this.Gamma); + vector.Y = (float)Math.Pow(vector.Y, this.Gamma); + vector.Z = (float)Math.Pow(vector.Z, this.Gamma); + vector.W = (float)Math.Pow(vector.W, this.Gamma); + } + + PixelOperations.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan); + } + }); + } + + /// + /// Applies the inverse gamma correction/highlight pass, and converts the input buffer into pixel values. + /// + /// The target pixels to apply the process to. + /// The source values. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// The + private void ApplyInverseGammaExposure( + Buffer2D targetPixels, + Buffer2D sourceValues, + Rectangle sourceRectangle, + Configuration configuration) + { + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + int width = workingRectangle.Width; + + ParallelHelper.IterateRows( + workingRectangle, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = sourceValues.GetRowSpan(y).Slice(startX); + Span targetPixelSpan = targetPixels.GetRowSpan(y).Slice(startX); + + for (int x = 0; x < width; x++) + { + ref Vector4 vector = ref targetRowSpan[x]; + vector.X = (float)Math.Pow(vector.X.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); + vector.Y = (float)Math.Pow(vector.Y.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); + vector.Z = (float)Math.Pow(vector.Z.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); + vector.W = (float)Math.Pow(vector.W.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); + } + + PixelOperations.Instance.FromVector4(configuration, targetRowSpan, targetPixelSpan); + } + }); + } } } From a261f93fff0f1cb845e95fd65e87dc05fd774a52 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Feb 2019 21:54:06 +0100 Subject: [PATCH 24/82] Speed optimizations, fixed partials computations in target rectangle --- .../Convolution/BokehBlurProcessor.cs | 93 +++++++++++++++---- 1 file changed, 73 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 88b549a8d7..523025ce1a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -32,9 +32,14 @@ internal class BokehBlurProcessor : ImageProcessor private readonly int componentsCount; /// - /// The kernel components to use for the current instance (a: X, b: Y, A: Z, B: W) + /// The kernel parameters to use for the current instance (a: X, b: Y, A: Z, B: W) /// - private readonly IReadOnlyList kernelParameters; + private readonly Vector4[] kernelParameters; + + /// + /// The kernel components for the current instance + /// + private readonly DenseMatrix[] kernels; /// /// The scaling factor for kernel values @@ -44,8 +49,8 @@ internal class BokehBlurProcessor : ImageProcessor /// /// The mapping of initialized complex kernels and parameters, to speed up the initialization of new instances /// - private static readonly Dictionary<(int, int), (IReadOnlyList, float, IReadOnlyList>)> Cache = - new Dictionary<(int, int), (IReadOnlyList, float, IReadOnlyList>)>(); + private static readonly Dictionary<(int, int), (Vector4[], float, DenseMatrix[])> Cache = + new Dictionary<(int, int), (Vector4[], float, DenseMatrix[])>(); /// /// Initializes a new instance of the class. @@ -69,23 +74,23 @@ public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) this.Gamma = gamma; // Reuse the initialized values from the cache, if possible - if (Cache.TryGetValue((radius, components), out (IReadOnlyList, float, IReadOnlyList>) info)) + if (Cache.TryGetValue((radius, components), out (Vector4[], float, DenseMatrix[]) info)) { this.kernelParameters = info.Item1; this.kernelsScale = info.Item2; - this.Kernels = info.Item3; + this.kernels = info.Item3; } else { // Initialize the complex kernels and parameters with the current arguments (this.kernelParameters, this.kernelsScale) = this.GetParameters(); - this.Kernels = ( + this.kernels = ( from parameters in this.kernelParameters select this.CreateComplex1DKernel(parameters.X, parameters.Y)).ToArray(); this.NormalizeKernels(); // Store them in the cache for future use - Cache.Add((radius, components), (this.kernelParameters, this.kernelsScale, this.Kernels)); + Cache.Add((radius, components), (this.kernelParameters, this.kernelsScale, this.kernels)); } } @@ -97,7 +102,12 @@ from parameters in this.kernelParameters /// /// Gets the complex kernels to use to apply the blur for the current instance /// - public IReadOnlyList> Kernels { get; } + public IReadOnlyList> Kernels => this.kernels; + + /// + /// Gets the kernel parameters used to compute the pixel values from each complex pixel + /// + public IReadOnlyList KernelParameters => this.kernelParameters; /// /// Gets the gamma highlight factor to use when applying the effect @@ -166,7 +176,7 @@ from parameters in this.kernelParameters /// /// Gets the kernel parameters and scaling factor for the current count value in the current instance /// - private (IReadOnlyList Parameters, float Scale) GetParameters() + private (Vector4[] Parameters, float Scale) GetParameters() { // Prepare the kernel components int index = Math.Max(0, Math.Min(this.componentsCount - 1, KernelComponents.Count)); @@ -254,8 +264,7 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size())) { // Apply the complex 1D convolutions - Span processingSpan = processing.Span; - this.OnFrameApplyCore(source, processingSpan, sourceRectangle, configuration); + this.OnFrameApplyCore(source, processing, sourceRectangle, configuration); // Apply the inverse gamma exposure pass, and write the final pixel data this.ApplyInverseGammaExposure(source.PixelBuffer, processing, sourceRectangle, configuration); @@ -266,13 +275,13 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source /// Applies the actual bokeh blur effect on the target image frame /// /// The source image. Cannot be null. - /// The target with the buffer to write to. + /// The target to write to. /// The structure that specifies the portion of the image object to draw. /// The configuration. - private void OnFrameApplyCore(ImageFrame source, Span processingSpan, Rectangle sourceRectangle, Configuration configuration) + private void OnFrameApplyCore(ImageFrame source, Buffer2D processing, Rectangle sourceRectangle, Configuration configuration) { // Perform two 1D convolutions for each component in the current instance - foreach ((DenseMatrix kernel, Vector4 parameters) in this.Kernels.Zip(this.kernelParameters, (k, p) => (k, p))) + for (int i = 0; i < this.kernels.Length; i++) { using (Buffer2D partialValues = configuration.MemoryAllocator.Allocate2D(source.Size())) { @@ -280,16 +289,14 @@ private void OnFrameApplyCore(ImageFrame source, Span processin using (Buffer2D firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) { var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + DenseMatrix kernel = this.kernels[i]; this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); this.ApplyConvolution(partialValues, firstPassValues, interest, kernel.Reshape(kernel.Count, 1), configuration); } // Add the results of the convolution with the current kernel - Span partialSpan = partialValues.Span; - for (int i = 0; i < processingSpan.Length; i++) - { - processingSpan[i] += partialSpan[i].WeightedSum(parameters.Z, parameters.W); - } + Vector4 parameters = this.kernelParameters[i]; + this.SumProcessingPartials(processing, partialValues, sourceRectangle, configuration, parameters.Z, parameters.W); } } } @@ -480,5 +487,51 @@ private void ApplyInverseGammaExposure( } }); } + + /// + /// Applies the process to the specified portion of the specified at the specified location + /// and with the specified size. + /// + /// The target instance to use to store the results. + /// The source complex pixels. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// The + /// The weight factor for the real component of the complex pixel values. + /// The weight factor for the imaginary component of the complex pixel values. + private void SumProcessingPartials( + Buffer2D targetValues, + Buffer2D sourceValues, + Rectangle sourceRectangle, + Configuration configuration, + float z, + float w) + { + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + int width = workingRectangle.Width; + + ParallelHelper.IterateRows( + workingRectangle, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); + Span sourceRowSpan = sourceValues.GetRowSpan(y).Slice(startX); + + for (int x = 0; x < width; x++) + { + targetRowSpan[x] += sourceRowSpan[x].WeightedSum(z, w); + } + } + }); + } } } From b36a90781a0cdad03efb41d208dd87c29b744648 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 23 Feb 2019 01:18:08 +0100 Subject: [PATCH 25/82] Increased epsilon value in the unit tests --- .../Processing/Processors/Convolution/BokehBlurTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index ed2a6ab279..3d92630b71 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -58,8 +58,8 @@ public void VerifyComplexComponents() Assert.Equal(spanA.Length, spanB.Length); for (int i = 0; i < spanA.Length; i++) { - Assert.True(Math.Abs(Math.Abs(spanA[i].Real) - Math.Abs(spanB[i].Real)) < 0.00000001f); - Assert.True(Math.Abs(Math.Abs(spanA[i].Imaginary) - Math.Abs(spanB[i].Imaginary)) < 0.00000001f); + Assert.True(Math.Abs(Math.Abs(spanA[i].Real) - Math.Abs(spanB[i].Real)) < 0.0001f); + Assert.True(Math.Abs(Math.Abs(spanA[i].Imaginary) - Math.Abs(spanB[i].Imaginary)) < 0.0001f); } } } From 5b8579ba486197ab3f3b06048ef8f5a8a22630dc Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 23 Feb 2019 15:38:19 +0100 Subject: [PATCH 26/82] Fixed for alpha transparency blur --- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 523025ce1a..d6c3c541cf 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -433,7 +433,7 @@ private void ApplyGammaExposure( vector.X = (float)Math.Pow(vector.X, this.Gamma); vector.Y = (float)Math.Pow(vector.Y, this.Gamma); vector.Z = (float)Math.Pow(vector.Z, this.Gamma); - vector.W = (float)Math.Pow(vector.W, this.Gamma); + Vector4Utils.Premultiply(ref vector); } PixelOperations.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan); @@ -480,7 +480,8 @@ private void ApplyInverseGammaExposure( vector.X = (float)Math.Pow(vector.X.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); vector.Y = (float)Math.Pow(vector.Y.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); vector.Z = (float)Math.Pow(vector.Z.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); - vector.W = (float)Math.Pow(vector.W.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); + + Vector4Utils.UnPremultiply(ref vector); } PixelOperations.Instance.FromVector4(configuration, targetRowSpan, targetPixelSpan); From bf090ada841f7b5f4bc5b4b54375d2a3c5841d7f Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 23 Feb 2019 16:09:53 +0100 Subject: [PATCH 27/82] Fixed a bug when only blurring a target rectangle --- src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs | 1 - .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 2b2d79c732..d7c1b8b6a0 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index d6c3c541cf..ddccf56edb 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -433,6 +433,7 @@ private void ApplyGammaExposure( vector.X = (float)Math.Pow(vector.X, this.Gamma); vector.Y = (float)Math.Pow(vector.Y, this.Gamma); vector.Z = (float)Math.Pow(vector.Z, this.Gamma); + Vector4Utils.Premultiply(ref vector); } @@ -484,7 +485,7 @@ private void ApplyInverseGammaExposure( Vector4Utils.UnPremultiply(ref vector); } - PixelOperations.Instance.FromVector4(configuration, targetRowSpan, targetPixelSpan); + PixelOperations.Instance.FromVector4(configuration, targetRowSpan.Slice(0, width), targetPixelSpan); } }); } From 34308fe74748db19e7538ea22dcf8616181bac4f Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 23 Feb 2019 16:47:16 +0100 Subject: [PATCH 28/82] Added bokeh blur image tests (WIP) --- .../Processors/Convolution/BokehBlurTest.cs | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index 3d92630b71..b607fa4691 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -8,7 +8,10 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Convolution; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.Primitives; using Xunit; @@ -16,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { public class BokehBlurTest : FileTestBase { - private static readonly string Components10x2 =@" + private static readonly string Components10x2 = @" [[ 0.00451261+0.0165137j 0.02161237-0.00299122j 0.00387479-0.02682816j -0.02752798-0.01788438j -0.03553877+0.0154543j -0.01428268+0.04224722j 0.01747482+0.04687464j 0.04243676+0.03451751j 0.05564306+0.01742537j @@ -63,5 +66,50 @@ public void VerifyComplexComponents() } } } + + public sealed class BokehBlurInfo + { + public int Radius { get; set; } + + public int Components { get; set; } + + public float Gamma { get; set; } + } + + public static readonly TheoryData BokehBlurValues = new TheoryData + { + new BokehBlurInfo { Radius = 8, Components = 1, Gamma = 1 }, + new BokehBlurInfo { Radius = 16, Components = 1, Gamma = 3 }, + new BokehBlurInfo { Radius = 16, Components = 2, Gamma = 3 } + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BokehBlurValues), DefaultPixelType)] + public void ImageShouldApplyBokehBlurFilter(TestImageProvider provider, BokehBlurInfo value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.BokehBlur(value.Radius, value.Components, value.Gamma)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BokehBlurValues), DefaultPixelType)] + public void ImageShouldApplyBokehBlurFilterInBox(TestImageProvider provider, BokehBlurInfo value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.BokehBlur(value.Radius, value.Components, value.Gamma, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } } } From c2486eb5a69ae878a510281ed803c9fe30ba88a1 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 23 Feb 2019 18:04:31 +0100 Subject: [PATCH 29/82] Added IXunitSerializable interface to the test info class --- .../Processors/Convolution/BokehBlurTest.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index b607fa4691..43bc99c473 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -14,6 +14,7 @@ using SixLabors.Primitives; using Xunit; +using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { @@ -67,13 +68,27 @@ public void VerifyComplexComponents() } } - public sealed class BokehBlurInfo + public sealed class BokehBlurInfo : IXunitSerializable { public int Radius { get; set; } public int Components { get; set; } public float Gamma { get; set; } + + public void Deserialize(IXunitSerializationInfo info) + { + this.Radius = info.GetValue(nameof(this.Radius)); + this.Components = info.GetValue(nameof(this.Components)); + this.Gamma = info.GetValue(nameof(this.Gamma)); + } + + public void Serialize(IXunitSerializationInfo info) + { + info.AddValue(nameof(this.Radius), this.Radius, typeof(int)); + info.AddValue(nameof(this.Components), this.Components, typeof(int)); + info.AddValue(nameof(this.Gamma), this.Gamma, typeof(float)); + } } public static readonly TheoryData BokehBlurValues = new TheoryData From 1fe7a43cb80d1f6b02daef473491d990def3092b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 23 Feb 2019 18:16:44 +0100 Subject: [PATCH 30/82] culture independent parsing in BokehBlurTest.cs --- .../Processing/Processors/Convolution/BokehBlurTest.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index 43bc99c473..1ddd61b86a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text.RegularExpressions; @@ -47,8 +48,10 @@ public void VerifyComplexComponents() Complex64[] component = values.Select( value => { - Match pair = Regex.Match(value.Replace('.', ','), @"([+-]?\d+,\d+)([+-]?\d+,\d+)j"); - return new Complex64(float.Parse(pair.Groups[1].Value), float.Parse(pair.Groups[2].Value)); + Match pair = Regex.Match(value, @"([+-]?\d+\.\d+)([+-]?\d+\.\d+)j"); + return new Complex64( + float.Parse(pair.Groups[1].Value, CultureInfo.InvariantCulture), + float.Parse(pair.Groups[2].Value, CultureInfo.InvariantCulture)); }).ToArray(); components.Add(component); } From fb4efe54e7e6516236b5976afb9113b92942db66 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 23 Feb 2019 20:39:55 +0100 Subject: [PATCH 31/82] Performance optimizations in the bokeh processor --- .../Convolution/BokehBlurProcessor.cs | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index ddccf56edb..e9df500dab 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.ParallelUtils; @@ -426,15 +428,15 @@ private void ApplyGammaExposure( { Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan); + Vector4Utils.Premultiply(vectorSpan); + ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectorSpan); for (int x = 0; x < width; x++) { - ref Vector4 vector = ref vectorSpan[x]; - vector.X = (float)Math.Pow(vector.X, this.Gamma); - vector.Y = (float)Math.Pow(vector.Y, this.Gamma); - vector.Z = (float)Math.Pow(vector.Z, this.Gamma); - - Vector4Utils.Premultiply(ref vector); + ref Vector4 v = ref Unsafe.Add(ref baseRef, x); + v.X = (float)Math.Pow(v.X, this.Gamma); + v.Y = (float)Math.Pow(v.Y, this.Gamma); + v.Z = (float)Math.Pow(v.Z, this.Gamma); } PixelOperations.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan); @@ -474,17 +476,17 @@ private void ApplyInverseGammaExposure( { Span targetRowSpan = sourceValues.GetRowSpan(y).Slice(startX); Span targetPixelSpan = targetPixels.GetRowSpan(y).Slice(startX); + ref Vector4 baseRef = ref MemoryMarshal.GetReference(targetRowSpan); for (int x = 0; x < width; x++) { - ref Vector4 vector = ref targetRowSpan[x]; - vector.X = (float)Math.Pow(vector.X.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); - vector.Y = (float)Math.Pow(vector.Y.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); - vector.Z = (float)Math.Pow(vector.Z.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); - - Vector4Utils.UnPremultiply(ref vector); + ref Vector4 v = ref Unsafe.Add(ref baseRef, x); + v.X = (float)Math.Pow(v.X.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); + v.Y = (float)Math.Pow(v.Y.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); + v.Z = (float)Math.Pow(v.Z.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); } + Vector4Utils.UnPremultiply(targetRowSpan); PixelOperations.Instance.FromVector4(configuration, targetRowSpan.Slice(0, width), targetPixelSpan); } }); @@ -527,10 +529,11 @@ private void SumProcessingPartials( { Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); Span sourceRowSpan = sourceValues.GetRowSpan(y).Slice(startX); + ref Vector4 baseRef = ref MemoryMarshal.GetReference(targetRowSpan); for (int x = 0; x < width; x++) { - targetRowSpan[x] += sourceRowSpan[x].WeightedSum(z, w); + Unsafe.Add(ref baseRef, x) += sourceRowSpan[x].WeightedSum(z, w); } } }); From 9711d5f22f0820a5b29802ec4152258fd35a4bf0 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 23 Feb 2019 21:45:49 +0100 Subject: [PATCH 32/82] Reduced number of memory allocations, fixed bug with multiple components --- .../Convolution/BokehBlurProcessor.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index e9df500dab..67a59c82f0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -283,18 +283,17 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source private void OnFrameApplyCore(ImageFrame source, Buffer2D processing, Rectangle sourceRectangle, Configuration configuration) { // Perform two 1D convolutions for each component in the current instance - for (int i = 0; i < this.kernels.Length; i++) + using (Buffer2D + partialValues = configuration.MemoryAllocator.Allocate2D(source.Size()), + firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) { - using (Buffer2D partialValues = configuration.MemoryAllocator.Allocate2D(source.Size())) + for (int i = 0; i < this.kernels.Length; i++) { // Compute the resulting complex buffer for the current component - using (Buffer2D firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) - { - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - DenseMatrix kernel = this.kernels[i]; - this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); - this.ApplyConvolution(partialValues, firstPassValues, interest, kernel.Reshape(kernel.Count, 1), configuration); - } + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + DenseMatrix kernel = this.kernels[i]; + this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); + this.ApplyConvolution(partialValues, firstPassValues, interest, kernel.Reshape(kernel.Count, 1), configuration); // Add the results of the convolution with the current kernel Vector4 parameters = this.kernelParameters[i]; From 697766ef9d1d16551e3c811d52c7bae659603a45 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 23 Feb 2019 22:11:09 +0100 Subject: [PATCH 33/82] Initialization and other speed improvements --- .../Convolution/BokehBlurProcessor.cs | 73 ++++++++----------- 1 file changed, 31 insertions(+), 42 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 67a59c82f0..dadfbd007a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -124,54 +124,54 @@ from parameters in this.kernelParameters /// /// Gets the available bokeh blur kernel parameters /// - private static IReadOnlyList KernelComponents { get; } = new[] + private static IReadOnlyList KernelComponents { get; } = new[] { // 1 component - new[,] { { 0.862325f, 1.624835f, 0.767583f, 1.862321f } }, + new[] { new Vector4(0.862325f, 1.624835f, 0.767583f, 1.862321f) }, // 2 components - new[,] + new[] { - { 0.886528f, 5.268909f, 0.411259f, -0.548794f }, - { 1.960518f, 1.558213f, 0.513282f, 4.56111f } + new Vector4(0.886528f, 5.268909f, 0.411259f, -0.548794f), + new Vector4(1.960518f, 1.558213f, 0.513282f, 4.56111f) }, // 3 components - new[,] + new[] { - { 2.17649f, 5.043495f, 1.621035f, -2.105439f }, - { 1.019306f, 9.027613f, -0.28086f, -0.162882f }, - { 2.81511f, 1.597273f, -0.366471f, 10.300301f } + new Vector4(2.17649f, 5.043495f, 1.621035f, -2.105439f), + new Vector4(1.019306f, 9.027613f, -0.28086f, -0.162882f), + new Vector4(2.81511f, 1.597273f, -0.366471f, 10.300301f) }, // 4 components - new[,] + new[] { - { 4.338459f, 1.553635f, -5.767909f, 46.164397f }, - { 3.839993f, 4.693183f, 9.795391f, -15.227561f }, - { 2.791880f, 8.178137f, -3.048324f, 0.302959f }, - { 1.342190f, 12.328289f, 0.010001f, 0.244650f } + new Vector4(4.338459f, 1.553635f, -5.767909f, 46.164397f), + new Vector4(3.839993f, 4.693183f, 9.795391f, -15.227561f), + new Vector4(2.791880f, 8.178137f, -3.048324f, 0.302959f), + new Vector4(1.342190f, 12.328289f, 0.010001f, 0.244650f) }, // 5 components - new[,] + new[] { - { 4.892608f, 1.685979f, -22.356787f, 85.91246f }, - { 4.71187f, 4.998496f, 35.918936f, -28.875618f }, - { 4.052795f, 8.244168f, -13.212253f, -1.578428f }, - { 2.929212f, 11.900859f, 0.507991f, 1.816328f }, - { 1.512961f, 16.116382f, 0.138051f, -0.01f } + new Vector4(4.892608f, 1.685979f, -22.356787f, 85.91246f), + new Vector4(4.71187f, 4.998496f, 35.918936f, -28.875618f), + new Vector4(4.052795f, 8.244168f, -13.212253f, -1.578428f), + new Vector4(2.929212f, 11.900859f, 0.507991f, 1.816328f), + new Vector4(1.512961f, 16.116382f, 0.138051f, -0.01f) }, // 6 components - new[,] + new[] { - { 5.143778f, 2.079813f, -82.326596f, 111.231024f }, - { 5.612426f, 6.153387f, 113.878661f, 58.004879f }, - { 5.982921f, 9.802895f, 39.479083f, -162.028887f }, - { 6.505167f, 11.059237f, -71.286026f, 95.027069f }, - { 3.869579f, 14.81052f, 1.405746f, -3.704914f }, - { 2.201904f, 19.032909f, -0.152784f, -0.107988f } + new Vector4(5.143778f, 2.079813f, -82.326596f, 111.231024f), + new Vector4(5.612426f, 6.153387f, 113.878661f, 58.004879f), + new Vector4(5.982921f, 9.802895f, 39.479083f, -162.028887f), + new Vector4(6.505167f, 11.059237f, -71.286026f, 95.027069f), + new Vector4(3.869579f, 14.81052f, 1.405746f, -3.704914f), + new Vector4(2.201904f, 19.032909f, -0.152784f, -0.107988f) } }; @@ -182,19 +182,7 @@ from parameters in this.kernelParameters { // Prepare the kernel components int index = Math.Max(0, Math.Min(this.componentsCount - 1, KernelComponents.Count)); - float[,] parameters = KernelComponents[index]; - var mapping = new Vector4[parameters.GetLength(0)]; - for (int i = 0; i < parameters.GetLength(0); i++) - { - mapping[i] = new Vector4( - parameters[i, 0], - parameters[i, 1], - parameters[i, 2], - parameters[i, 3]); - } - - // Return the components and the adjustment scale - return (mapping, KernelScales[index]); + return (KernelComponents[index], KernelScales[index]); } /// @@ -528,11 +516,12 @@ private void SumProcessingPartials( { Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); Span sourceRowSpan = sourceValues.GetRowSpan(y).Slice(startX); - ref Vector4 baseRef = ref MemoryMarshal.GetReference(targetRowSpan); + ref Vector4 baseTargetRef = ref MemoryMarshal.GetReference(targetRowSpan); + ref ComplexVector4 baseSourceRef = ref MemoryMarshal.GetReference(sourceRowSpan); for (int x = 0; x < width; x++) { - Unsafe.Add(ref baseRef, x) += sourceRowSpan[x].WeightedSum(z, w); + Unsafe.Add(ref baseTargetRef, x) += Unsafe.Add(ref baseSourceRef, x).WeightedSum(z, w); } } }); From bd348a3475913b06c5fdc2fe099a3407015780c7 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 23 Feb 2019 22:44:34 +0100 Subject: [PATCH 34/82] More initialization speed improvements --- .../Convolution/BokehBlurProcessor.cs | 56 +++++++++++-------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index dadfbd007a..8b77d8f469 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -192,22 +192,20 @@ from parameters in this.kernelParameters /// The angle component for each complex component private DenseMatrix CreateComplex1DKernel(float a, float b) { - // Precompute the range values - float[] ax = Enumerable.Range(-this.Radius, (this.Radius * 2) + 1).Select( - i => - { - float value = i * this.kernelsScale * (1f / this.Radius); - return value * value; - }).ToArray(); - - // Compute the complex kernels var kernel = new DenseMatrix(1, this.kernelSize); - for (int i = 0; i < this.kernelSize; i++) + ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel.Span); + int r = this.Radius, n = -r; + + for (int i = 0; i < this.kernelSize; i++, n++) { - float - real = (float)(Math.Exp(-a * ax[i]) * Math.Cos(b * ax[i])), - imaginary = (float)(Math.Exp(-a * ax[i]) * Math.Sin(b * ax[i])); - kernel[i, 0] = new Complex64(real, imaginary); + // Incrementally compute the range values + float value = n * this.kernelsScale * (1f / r); + value *= value; + + // Fill in the complex kernel values + Unsafe.Add(ref baseRef, i) = new Complex64( + (float)(Math.Exp(-a * value) * Math.Cos(b * value)), + (float)(Math.Exp(-a * value) * Math.Sin(b * value))); } return kernel; @@ -220,26 +218,40 @@ private void NormalizeKernels() { // Calculate the complex weighted sum double total = 0; - foreach ((DenseMatrix kernel, Vector4 param) in this.Kernels.Zip(this.kernelParameters, (k, p) => (k, p))) + Span> kernelsSpan = this.kernels.AsSpan(); + ref DenseMatrix baseKernelsRef = ref MemoryMarshal.GetReference(kernelsSpan); + ref Vector4 baseParamsRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan()); + + for (int i = 0; i < this.kernelParameters.Length; i++) { - for (int i = 0; i < kernel.Count; i++) + ref DenseMatrix matrixRef = ref Unsafe.Add(ref baseKernelsRef, i); + int length = matrixRef.Count; + ref Complex64 kernelRef = ref MemoryMarshal.GetReference(matrixRef.Span); + ref Vector4 paramsRef = ref Unsafe.Add(ref baseParamsRef, i); + + for (int j = 0; j < length; j++) { - for (int j = 0; j < kernel.Count; j++) + for (int k = 0; k < length; k++) { + ref Complex64 jRef = ref Unsafe.Add(ref kernelRef, j); + ref Complex64 kRef = ref Unsafe.Add(ref kernelRef, k); total += - (param.Z * ((kernel[i, 0].Real * kernel[j, 0].Real) - (kernel[i, 0].Imaginary * kernel[j, 0].Imaginary))) + - (param.W * ((kernel[i, 0].Real * kernel[j, 0].Imaginary) + (kernel[i, 0].Imaginary * kernel[j, 0].Real))); + (paramsRef.Z * ((jRef.Real * kRef.Real) - (jRef.Imaginary * kRef.Imaginary))) + + (paramsRef.W * ((jRef.Real * kRef.Imaginary) + (jRef.Imaginary * kRef.Real))); } } } // Normalize the kernels float scalar = (float)(1f / Math.Sqrt(total)); - foreach (DenseMatrix kernel in this.Kernels) + for (int i = 0; i < kernelsSpan.Length; i++) { - for (int i = 0; i < kernel.Count; i++) + ref DenseMatrix matrixRef = ref Unsafe.Add(ref baseKernelsRef, i); + ref Complex64 valueRef = ref MemoryMarshal.GetReference(matrixRef.Span); + + for (int j = 0; j < matrixRef.Count; j++) { - kernel[i, 0] *= scalar; + Unsafe.Add(ref valueRef, j) *= scalar; } } } From ee5bbc3f3fb4550f93a6e07c9419fb10e7a9216e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 23 Feb 2019 22:52:39 +0100 Subject: [PATCH 35/82] Replaced LINQ with manual loop --- .../Processors/Convolution/BokehBlurProcessor.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 8b77d8f469..503b13a786 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -86,9 +85,15 @@ public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) { // Initialize the complex kernels and parameters with the current arguments (this.kernelParameters, this.kernelsScale) = this.GetParameters(); - this.kernels = ( - from parameters in this.kernelParameters - select this.CreateComplex1DKernel(parameters.X, parameters.Y)).ToArray(); + + this.kernels = new DenseMatrix[this.kernelParameters.Length]; + ref Vector4 baseRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan()); + for (int i = 0; i < this.kernelParameters.Length; i++) + { + ref Vector4 paramsRef = ref Unsafe.Add(ref baseRef, i); + this.kernels[i] = this.CreateComplex1DKernel(paramsRef.X, paramsRef.Y); + } + this.NormalizeKernels(); // Store them in the cache for future use @@ -219,7 +224,7 @@ private void NormalizeKernels() // Calculate the complex weighted sum double total = 0; Span> kernelsSpan = this.kernels.AsSpan(); - ref DenseMatrix baseKernelsRef = ref MemoryMarshal.GetReference(kernelsSpan); + ref DenseMatrix baseKernelsRef = ref MemoryMarshal.GetReference(kernelsSpan); ref Vector4 baseParamsRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan()); for (int i = 0; i < this.kernelParameters.Length; i++) From a64636e82b11d33b6f4b78e3f0013359f26987ef Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 24 Feb 2019 00:03:29 +0100 Subject: [PATCH 36/82] Added BokehBlur overload to just specify the target bounds --- src/ImageSharp/Processing/BokehBlurExtensions.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/ImageSharp/Processing/BokehBlurExtensions.cs b/src/ImageSharp/Processing/BokehBlurExtensions.cs index ea251794a7..62c049d8bc 100644 --- a/src/ImageSharp/Processing/BokehBlurExtensions.cs +++ b/src/ImageSharp/Processing/BokehBlurExtensions.cs @@ -35,6 +35,19 @@ public static IImageProcessingContext BokehBlur(this IImageProce where TPixel : struct, IPixel => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma)); + /// + /// Applies a bokeh blur to the image. + /// + /// The pixel format. + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle) + where TPixel : struct, IPixel + => source.ApplyProcessor(new BokehBlurProcessor(), rectangle); + /// /// Applies a bokeh blur to the image. /// From e18066ed7c59374e80d5146e1846918249cbbca1 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 24 Feb 2019 00:24:46 +0100 Subject: [PATCH 37/82] Speed optimizations to the bokeh 1D convolutions --- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 503b13a786..a651eba081 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -347,7 +347,7 @@ private void ApplyConvolution( for (int x = 0; x < width; x++) { - DenseMatrixUtils.Convolve(in matrix, sourcePixels, targetRowSpan, y, x, maxY, maxX, startX); + DenseMatrixUtils.Convolve1D(matrix.Span, sourcePixels, targetRowSpan, y, x, maxY, maxX, startX); } } }); @@ -393,7 +393,7 @@ private void ApplyConvolution( for (int x = 0; x < width; x++) { - DenseMatrixUtils.Convolve(in matrix, sourceValues, targetRowSpan, y, x, maxY, maxX, startX); + DenseMatrixUtils.Convolve1D(matrix.Span, sourceValues, targetRowSpan, y, x, maxY, maxX, startX); } } }); From 1122563a1aaada2c4079d6b5fa9af878cc637313 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 24 Feb 2019 00:43:47 +0100 Subject: [PATCH 38/82] More speed optimizations to the bokeh processor --- src/ImageSharp/Primitives/DenseMatrix{T}.cs | 31 ---------- .../Convolution/BokehBlurProcessor.cs | 57 +++++++++---------- .../Processors/Convolution/BokehBlurTest.cs | 4 +- 3 files changed, 30 insertions(+), 62 deletions(-) diff --git a/src/ImageSharp/Primitives/DenseMatrix{T}.cs b/src/ImageSharp/Primitives/DenseMatrix{T}.cs index 11ba5a96f4..170292e29e 100644 --- a/src/ImageSharp/Primitives/DenseMatrix{T}.cs +++ b/src/ImageSharp/Primitives/DenseMatrix{T}.cs @@ -51,25 +51,6 @@ public DenseMatrix(int length) { } - /// - /// Initializes a new instance of the struct. - /// - /// The array to provide access to. - /// The number of columns. - /// The number of rows. - private DenseMatrix(T[] data, int columns, int rows) - { - Guard.MustBeGreaterThan(columns, 0, nameof(columns)); - Guard.MustBeGreaterThan(rows, 0, nameof(rows)); - Guard.MustBeLessThanOrEqualTo(rows * columns, data.Length, nameof(data)); - - this.Rows = rows; - this.Columns = columns; - this.Size = new Size(columns, rows); - this.Count = columns * rows; - this.Data = data; - } - /// /// Initializes a new instance of the struct. /// @@ -194,18 +175,6 @@ public DenseMatrix Transpose() return result; } - /// - /// Reshapes the current memory, without performing copy operations. - /// - /// The new width of the instance to create. - /// The new height of the instance to create. - [MethodImpl(InliningOptions.ShortMethod)] - internal DenseMatrix Reshape(int columns, int rows) - { - SixLabors.DebugGuard.MustBeLessThanOrEqualTo(columns * rows, this.Columns * this.Rows, nameof(columns)); - return new DenseMatrix(this.Data, columns, rows); - } - /// /// Fills the matrix with the given value /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index a651eba081..6a73f2c80a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -40,7 +40,7 @@ internal class BokehBlurProcessor : ImageProcessor /// /// The kernel components for the current instance /// - private readonly DenseMatrix[] kernels; + private readonly Complex64[][] kernels; /// /// The scaling factor for kernel values @@ -50,8 +50,7 @@ internal class BokehBlurProcessor : ImageProcessor /// /// The mapping of initialized complex kernels and parameters, to speed up the initialization of new instances /// - private static readonly Dictionary<(int, int), (Vector4[], float, DenseMatrix[])> Cache = - new Dictionary<(int, int), (Vector4[], float, DenseMatrix[])>(); + private static readonly Dictionary<(int, int), (Vector4[], float, Complex64[][])> Cache = new Dictionary<(int, int), (Vector4[], float, Complex64[][])>(); /// /// Initializes a new instance of the class. @@ -75,7 +74,7 @@ public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) this.Gamma = gamma; // Reuse the initialized values from the cache, if possible - if (Cache.TryGetValue((radius, components), out (Vector4[], float, DenseMatrix[]) info)) + if (Cache.TryGetValue((radius, components), out (Vector4[], float, Complex64[][]) info)) { this.kernelParameters = info.Item1; this.kernelsScale = info.Item2; @@ -86,7 +85,7 @@ public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) // Initialize the complex kernels and parameters with the current arguments (this.kernelParameters, this.kernelsScale) = this.GetParameters(); - this.kernels = new DenseMatrix[this.kernelParameters.Length]; + this.kernels = new Complex64[this.kernelParameters.Length][]; ref Vector4 baseRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan()); for (int i = 0; i < this.kernelParameters.Length; i++) { @@ -109,7 +108,7 @@ public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) /// /// Gets the complex kernels to use to apply the blur for the current instance /// - public IReadOnlyList> Kernels => this.kernels; + public IReadOnlyList Kernels => this.kernels; /// /// Gets the kernel parameters used to compute the pixel values from each complex pixel @@ -195,10 +194,10 @@ public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) /// /// The exponential parameter for each complex component /// The angle component for each complex component - private DenseMatrix CreateComplex1DKernel(float a, float b) + private Complex64[] CreateComplex1DKernel(float a, float b) { - var kernel = new DenseMatrix(1, this.kernelSize); - ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel.Span); + var kernel = new Complex64[this.kernelSize]; + ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel.AsSpan()); int r = this.Radius, n = -r; for (int i = 0; i < this.kernelSize; i++, n++) @@ -223,23 +222,23 @@ private void NormalizeKernels() { // Calculate the complex weighted sum double total = 0; - Span> kernelsSpan = this.kernels.AsSpan(); - ref DenseMatrix baseKernelsRef = ref MemoryMarshal.GetReference(kernelsSpan); + Span kernelsSpan = this.kernels.AsSpan(); + ref Complex64[] baseKernelsRef = ref MemoryMarshal.GetReference(kernelsSpan); ref Vector4 baseParamsRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan()); for (int i = 0; i < this.kernelParameters.Length; i++) { - ref DenseMatrix matrixRef = ref Unsafe.Add(ref baseKernelsRef, i); - int length = matrixRef.Count; - ref Complex64 kernelRef = ref MemoryMarshal.GetReference(matrixRef.Span); + ref Complex64[] kernelRef = ref Unsafe.Add(ref baseKernelsRef, i); + int length = kernelRef.Length; + ref Complex64 valueRef = ref kernelRef[0]; ref Vector4 paramsRef = ref Unsafe.Add(ref baseParamsRef, i); for (int j = 0; j < length; j++) { for (int k = 0; k < length; k++) { - ref Complex64 jRef = ref Unsafe.Add(ref kernelRef, j); - ref Complex64 kRef = ref Unsafe.Add(ref kernelRef, k); + ref Complex64 jRef = ref Unsafe.Add(ref valueRef, j); + ref Complex64 kRef = ref Unsafe.Add(ref valueRef, k); total += (paramsRef.Z * ((jRef.Real * kRef.Real) - (jRef.Imaginary * kRef.Imaginary))) + (paramsRef.W * ((jRef.Real * kRef.Imaginary) + (jRef.Imaginary * kRef.Real))); @@ -251,10 +250,11 @@ private void NormalizeKernels() float scalar = (float)(1f / Math.Sqrt(total)); for (int i = 0; i < kernelsSpan.Length; i++) { - ref DenseMatrix matrixRef = ref Unsafe.Add(ref baseKernelsRef, i); - ref Complex64 valueRef = ref MemoryMarshal.GetReference(matrixRef.Span); + ref Complex64[] kernelsRef = ref Unsafe.Add(ref baseKernelsRef, i); + int length = kernelsRef.Length; + ref Complex64 valueRef = ref kernelsRef[0]; - for (int j = 0; j < matrixRef.Count; j++) + for (int j = 0; j < length; j++) { Unsafe.Add(ref valueRef, j) *= scalar; } @@ -292,13 +292,14 @@ private void OnFrameApplyCore(ImageFrame source, Buffer2D proce partialValues = configuration.MemoryAllocator.Allocate2D(source.Size()), firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) { + ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan()); for (int i = 0; i < this.kernels.Length; i++) { // Compute the resulting complex buffer for the current component var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - DenseMatrix kernel = this.kernels[i]; + Complex64[] kernel = Unsafe.Add(ref baseRef, i); this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); - this.ApplyConvolution(partialValues, firstPassValues, interest, kernel.Reshape(kernel.Count, 1), configuration); + this.ApplyConvolution(partialValues, firstPassValues, interest, kernel, configuration); // Add the results of the convolution with the current kernel Vector4 parameters = this.kernelParameters[i]; @@ -316,16 +317,15 @@ private void OnFrameApplyCore(ImageFrame source, Buffer2D proce /// /// The structure that specifies the portion of the image object to draw. /// - /// The kernel operator. + /// The 1D kernel. /// The private void ApplyConvolution( Buffer2D targetValues, Buffer2D sourcePixels, Rectangle sourceRectangle, - in DenseMatrix kernel, + Complex64[] kernel, Configuration configuration) { - DenseMatrix matrix = kernel; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; @@ -347,7 +347,7 @@ private void ApplyConvolution( for (int x = 0; x < width; x++) { - DenseMatrixUtils.Convolve1D(matrix.Span, sourcePixels, targetRowSpan, y, x, maxY, maxX, startX); + DenseMatrixUtils.Convolve1D(kernel, sourcePixels, targetRowSpan, y, x, maxY, maxX, startX); } } }); @@ -362,16 +362,15 @@ private void ApplyConvolution( /// /// The structure that specifies the portion of the image object to draw. /// - /// The kernel operator. + /// The 1D kernel. /// The private void ApplyConvolution( Buffer2D targetValues, Buffer2D sourceValues, Rectangle sourceRectangle, - in DenseMatrix kernel, + Complex64[] kernel, Configuration configuration) { - DenseMatrix matrix = kernel; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; @@ -393,7 +392,7 @@ private void ApplyConvolution( for (int x = 0; x < width; x++) { - DenseMatrixUtils.Convolve1D(matrix.Span, sourceValues, targetRowSpan, y, x, maxY, maxX, startX); + DenseMatrixUtils.Convolve1D(kernel, sourceValues, targetRowSpan, y, x, maxY, maxX, startX); } } }); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index 1ddd61b86a..5517088450 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -59,9 +59,9 @@ public void VerifyComplexComponents() // Make sure the kernel components are the same var processor = new BokehBlurProcessor(10); Assert.Equal(components.Count, processor.Kernels.Count); - foreach ((Complex64[] a, DenseMatrix b) in components.Zip(processor.Kernels, (a, b) => (a, b))) + foreach ((Complex64[] a, Complex64[] b) in components.Zip(processor.Kernels, (a, b) => (a, b))) { - Span spanA = a.AsSpan(), spanB = b.Span; + Span spanA = a.AsSpan(), spanB = b.AsSpan(); Assert.Equal(spanA.Length, spanB.Length); for (int i = 0; i < spanA.Length; i++) { From afcf78a7c8121449f38d5180625af2588bfc9943 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 24 Feb 2019 03:16:55 +0100 Subject: [PATCH 39/82] Fixed code style and Complex64.ToString method --- src/ImageSharp/Primitives/Complex64.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Primitives/Complex64.cs b/src/ImageSharp/Primitives/Complex64.cs index 9ce6f4a4d7..75905a9bd7 100644 --- a/src/ImageSharp/Primitives/Complex64.cs +++ b/src/ImageSharp/Primitives/Complex64.cs @@ -66,9 +66,8 @@ public Complex64(float real, float imaginary) [MethodImpl(InliningOptions.ShortMethod)] public static ComplexVector4 operator *(Complex64 value, ComplexVector4 vector) { - Vector4 - real = (value.Real * vector.Real) - (value.Imaginary * vector.Imaginary), - imaginary = (value.Real * vector.Imaginary) + (value.Imaginary * vector.Real); + Vector4 real = (value.Real * vector.Real) - (value.Imaginary * vector.Imaginary); + Vector4 imaginary = (value.Real * vector.Imaginary) + (value.Imaginary * vector.Real); return new ComplexVector4 { Real = real, Imaginary = imaginary }; } @@ -91,6 +90,6 @@ public override int GetHashCode() } /// - public override string ToString() => $"{this.Real}+{this.Imaginary}j"; + public override string ToString() => $"{this.Real}{(this.Imaginary >= 0 ? "+" : string.Empty)}{this.Imaginary}j"; } } From b56da131011d4862bdb5352e86a9bde5688f2327 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 26 Feb 2019 19:44:25 +0100 Subject: [PATCH 40/82] Fixed processing buffer initialization --- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 6a73f2c80a..c9a4af3e86 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -11,6 +11,7 @@ using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; +using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution @@ -268,7 +269,7 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source this.ApplyGammaExposure(source.PixelBuffer, sourceRectangle, configuration); // Create a 0-filled buffer to use to store the result of the component convolutions - using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size())) + using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean)) { // Apply the complex 1D convolutions this.OnFrameApplyCore(source, processing, sourceRectangle, configuration); From 32d062deaa7b8b42a9d3c49ed6e78d940ce1b43b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 26 Feb 2019 22:15:13 +0100 Subject: [PATCH 41/82] Minor performance improvements --- .../Convolution/BokehBlurProcessor.cs | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index c9a4af3e86..ece707959d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -270,29 +270,11 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source // Create a 0-filled buffer to use to store the result of the component convolutions using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean)) - { - // Apply the complex 1D convolutions - this.OnFrameApplyCore(source, processing, sourceRectangle, configuration); - - // Apply the inverse gamma exposure pass, and write the final pixel data - this.ApplyInverseGammaExposure(source.PixelBuffer, processing, sourceRectangle, configuration); - } - } - - /// - /// Applies the actual bokeh blur effect on the target image frame - /// - /// The source image. Cannot be null. - /// The target to write to. - /// The structure that specifies the portion of the image object to draw. - /// The configuration. - private void OnFrameApplyCore(ImageFrame source, Buffer2D processing, Rectangle sourceRectangle, Configuration configuration) - { - // Perform two 1D convolutions for each component in the current instance using (Buffer2D partialValues = configuration.MemoryAllocator.Allocate2D(source.Size()), firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) { + // Perform two 1D convolutions for each component in the current instance ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan()); for (int i = 0; i < this.kernels.Length; i++) { @@ -306,6 +288,9 @@ private void OnFrameApplyCore(ImageFrame source, Buffer2D proce Vector4 parameters = this.kernelParameters[i]; this.SumProcessingPartials(processing, partialValues, sourceRectangle, configuration, parameters.Z, parameters.W); } + + // Apply the inverse gamma exposure pass, and write the final pixel data + this.ApplyInverseGammaExposure(source.PixelBuffer, processing, sourceRectangle, configuration); } } From 6e3e541bb738c606fdcaadf9ce29a66c8abc5eb4 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 9 Mar 2019 03:05:15 +0100 Subject: [PATCH 42/82] FIxed issue when applying bokeh blur to specific bounds --- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index ece707959d..6c0e98dbdf 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -333,7 +333,7 @@ private void ApplyConvolution( for (int x = 0; x < width; x++) { - DenseMatrixUtils.Convolve1D(kernel, sourcePixels, targetRowSpan, y, x, maxY, maxX, startX); + DenseMatrixUtils.Convolve1D(kernel, sourcePixels, targetRowSpan, y, x, startY, maxY, maxX, startX); } } }); From 39ce26c0034fbe1a17c7e0924f9a520ee83e9669 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 9 Mar 2019 13:41:19 +0100 Subject: [PATCH 43/82] Minor speed optimizaations --- .../Processors/Convolution/BokehBlurProcessor.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 6c0e98dbdf..c69d8d6567 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -404,6 +404,7 @@ private void ApplyGammaExposure( var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); int width = workingRectangle.Width; + float gamma = this.Gamma; ParallelHelper.IterateRowsWithTempBuffer( workingRectangle, @@ -423,9 +424,9 @@ private void ApplyGammaExposure( for (int x = 0; x < width; x++) { ref Vector4 v = ref Unsafe.Add(ref baseRef, x); - v.X = (float)Math.Pow(v.X, this.Gamma); - v.Y = (float)Math.Pow(v.Y, this.Gamma); - v.Z = (float)Math.Pow(v.Z, this.Gamma); + v.X = (float)Math.Pow(v.X, gamma); + v.Y = (float)Math.Pow(v.Y, gamma); + v.Z = (float)Math.Pow(v.Z, gamma); } PixelOperations.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan); @@ -455,6 +456,7 @@ private void ApplyInverseGammaExposure( var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); int width = workingRectangle.Width; + float expGamma = 1 / this.Gamma; ParallelHelper.IterateRows( workingRectangle, @@ -470,9 +472,9 @@ private void ApplyInverseGammaExposure( for (int x = 0; x < width; x++) { ref Vector4 v = ref Unsafe.Add(ref baseRef, x); - v.X = (float)Math.Pow(v.X.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); - v.Y = (float)Math.Pow(v.Y.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); - v.Z = (float)Math.Pow(v.Z.Clamp(0, float.PositiveInfinity), 1 / this.Gamma); + v.X = (float)Math.Pow(v.X.Clamp(0, float.PositiveInfinity), expGamma); + v.Y = (float)Math.Pow(v.Y.Clamp(0, float.PositiveInfinity), expGamma); + v.Z = (float)Math.Pow(v.Z.Clamp(0, float.PositiveInfinity), expGamma); } Vector4Utils.UnPremultiply(targetRowSpan); From 4f77a38136c8d876333e71eecf718747b9da055f Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 9 Mar 2019 13:47:02 +0100 Subject: [PATCH 44/82] Minor code refactoring --- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index c69d8d6567..a3480d58d5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -333,7 +333,7 @@ private void ApplyConvolution( for (int x = 0; x < width; x++) { - DenseMatrixUtils.Convolve1D(kernel, sourcePixels, targetRowSpan, y, x, startY, maxY, maxX, startX); + DenseMatrixUtils.Convolve1D(kernel, sourcePixels, targetRowSpan, y, x, startY, maxY, startX, maxX); } } }); @@ -378,7 +378,7 @@ private void ApplyConvolution( for (int x = 0; x < width; x++) { - DenseMatrixUtils.Convolve1D(kernel, sourceValues, targetRowSpan, y, x, maxY, maxX, startX); + DenseMatrixUtils.Convolve1D(kernel, sourceValues, targetRowSpan, y, x, maxY, startX, maxX); } } }); From 93b6a8256051041c003c5ecb31cd940b9cc81a3b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 15 Mar 2019 15:27:23 +0100 Subject: [PATCH 45/82] Fixed convolution upper bound in second 1D pass --- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index a3480d58d5..4dc6101a87 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -378,7 +378,7 @@ private void ApplyConvolution( for (int x = 0; x < width; x++) { - DenseMatrixUtils.Convolve1D(kernel, sourceValues, targetRowSpan, y, x, maxY, startX, maxX); + DenseMatrixUtils.Convolve1D(kernel, sourceValues, targetRowSpan, y, x, startY, maxY, startX, maxX); } } }); From d0b0b2b12b2d6762f761bea53de90f193f452a5b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 31 Mar 2019 21:21:04 +0200 Subject: [PATCH 46/82] improve BokehBlurTest coverage --- .../Processors/Convolution/BokehBlurTest.cs | 59 +++++++++++++------ 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index 5517088450..151c4d8870 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - public class BokehBlurTest : FileTestBase + public class BokehBlurTest { private static readonly string Components10x2 = @" [[ 0.00451261+0.0165137j 0.02161237-0.00299122j 0.00387479-0.02682816j @@ -92,6 +92,8 @@ public void Serialize(IXunitSerializationInfo info) info.AddValue(nameof(this.Components), this.Components, typeof(int)); info.AddValue(nameof(this.Gamma), this.Gamma, typeof(float)); } + + public override string ToString() => $"R{this.Radius}_C{this.Components}_G{this.Gamma}"; } public static readonly TheoryData BokehBlurValues = new TheoryData @@ -101,33 +103,54 @@ public void Serialize(IXunitSerializationInfo info) new BokehBlurInfo { Radius = 16, Components = 2, Gamma = 3 } }; + public static readonly string[] TestFiles = + { + TestImages.Png.CalliphoraPartial, + TestImages.Png.Bike, + TestImages.Png.BikeGrayscale, + TestImages.Png.Cross, + }; + [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BokehBlurValues), DefaultPixelType)] - public void ImageShouldApplyBokehBlurFilter(TestImageProvider provider, BokehBlurInfo value) + [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32)] + [WithSolidFilledImages(nameof(BokehBlurValues), 50, 50, "Red", PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BokehBlurValues), 200, 100, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BokehBlurValues), 23, 31, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BokehBlurValues), 30, 20, PixelTypes.Rgba32)] + public void BokehBlurFilterProcessor(TestImageProvider provider, BokehBlurInfo value) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.BokehBlur(value.Radius, value.Components, value.Gamma)); - image.DebugSave(provider, value); - } + provider.RunValidatingProcessorTest( + x => x.BokehBlur(value.Radius, value.Components, value.Gamma), + testOutputDetails: value.ToString(), + appendPixelTypeToFileName: false); } [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BokehBlurValues), DefaultPixelType)] - public void ImageShouldApplyBokehBlurFilterInBox(TestImageProvider provider, BokehBlurInfo value) + [WithTestPatternImages(200, 200, PixelTypes.Bgr24 | PixelTypes.Bgra32 | PixelTypes.Alpha8)] + public void BokehBlurFilterProcessor_WorksWithAllPixelTypes(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image source = provider.GetImage()) - using (Image image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + provider.RunValidatingProcessorTest( + x => x.BokehBlur(8, 2, 3), + appendSourceFileOrDescription: false); + } - image.Mutate(x => x.BokehBlur(value.Radius, value.Components, value.Gamma, bounds)); - image.DebugSave(provider, value); - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } + [Theory] + [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32)] + public void BokehBlurFilterProcessor_Bounded(TestImageProvider provider, BokehBlurInfo value) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest( + x => + { + Size size = x.GetCurrentSize(); + var bounds = new Rectangle(10, 10, size.Width / 2, size.Height / 2); + x.BokehBlur(value.Radius, value.Components, value.Gamma, bounds); + }, + testOutputDetails: value.ToString(), + appendPixelTypeToFileName: false); } } } From 3772e891b1371429cfebc67523801b4e77cffaf6 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 31 Mar 2019 21:28:39 +0200 Subject: [PATCH 47/82] use Gray8 instead of Alpha8 --- .../Processing/Processors/Convolution/BokehBlurTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index 151c4d8870..3215327e6f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -127,7 +127,7 @@ public void BokehBlurFilterProcessor(TestImageProvider provider, } [Theory] - [WithTestPatternImages(200, 200, PixelTypes.Bgr24 | PixelTypes.Bgra32 | PixelTypes.Alpha8)] + [WithTestPatternImages(200, 200, PixelTypes.Bgr24 | PixelTypes.Bgra32 | PixelTypes.Gray8)] public void BokehBlurFilterProcessor_WorksWithAllPixelTypes(TestImageProvider provider) where TPixel : struct, IPixel { From 2ed623ab69d466c15c56b301334afd2d27be6ba3 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 31 Mar 2019 22:12:33 +0200 Subject: [PATCH 48/82] Adjusted guard position in bokeh processor constructor --- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 4dc6101a87..380090e8ff 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -67,11 +67,11 @@ internal class BokehBlurProcessor : ImageProcessor /// public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) { + SixLabors.Guard.MustBeGreaterThanOrEqualTo(gamma, 1, nameof(gamma)); + this.Radius = radius; this.kernelSize = (radius * 2) + 1; this.componentsCount = components; - - SixLabors.Guard.MustBeGreaterThanOrEqualTo(gamma, 1, nameof(gamma)); this.Gamma = gamma; // Reuse the initialized values from the cache, if possible From c05d39ac098b7c263d73b25edf0eca4caa089923 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 31 Mar 2019 22:28:01 +0200 Subject: [PATCH 49/82] Added BokehBlurParameters struct --- .../Primitives/BokehBlurParameters.cs | 32 +++++++++++++++++++ .../Convolution/BokehBlurProcessor.cs | 7 ++-- 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 src/ImageSharp/Primitives/BokehBlurParameters.cs diff --git a/src/ImageSharp/Primitives/BokehBlurParameters.cs b/src/ImageSharp/Primitives/BokehBlurParameters.cs new file mode 100644 index 0000000000..1fc2111569 --- /dev/null +++ b/src/ImageSharp/Primitives/BokehBlurParameters.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Primitives +{ + /// + /// A that contains parameters to apply a bokeh blur filter + /// + public readonly struct BokehBlurParameters + { + /// + /// The size of the convolution kernel to use when applying the bokeh blur + /// + public readonly int Radius; + + /// + /// The number of complex components to use to approximate the bokeh kernel + /// + public readonly int Components; + + /// + /// Initializes a new instance of the struct. + /// + /// The size of the kernel + /// The number of kernel components + public BokehBlurParameters(int radius, int components) + { + this.Radius = radius; + this.Components = components; + } + } +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 380090e8ff..70a3e935c8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -51,7 +51,7 @@ internal class BokehBlurProcessor : ImageProcessor /// /// The mapping of initialized complex kernels and parameters, to speed up the initialization of new instances /// - private static readonly Dictionary<(int, int), (Vector4[], float, Complex64[][])> Cache = new Dictionary<(int, int), (Vector4[], float, Complex64[][])>(); + private static readonly Dictionary Cache = new Dictionary(); /// /// Initializes a new instance of the class. @@ -75,7 +75,8 @@ public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) this.Gamma = gamma; // Reuse the initialized values from the cache, if possible - if (Cache.TryGetValue((radius, components), out (Vector4[], float, Complex64[][]) info)) + var parameters = new BokehBlurParameters(radius, components); + if (Cache.TryGetValue(parameters, out (Vector4[], float, Complex64[][]) info)) { this.kernelParameters = info.Item1; this.kernelsScale = info.Item2; @@ -97,7 +98,7 @@ public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) this.NormalizeKernels(); // Store them in the cache for future use - Cache.Add((radius, components), (this.kernelParameters, this.kernelsScale, this.kernels)); + Cache.Add(parameters, (this.kernelParameters, this.kernelsScale, this.kernels)); } } From d511f063f59835a57905fa2b6409aa068cc38a29 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 31 Mar 2019 22:35:17 +0200 Subject: [PATCH 50/82] Added BokehBlurKernelData struct --- .../Primitives/BokehBlurKernelData.cs | 41 +++++++++++++++++++ .../Primitives/BokehBlurParameters.cs | 2 +- .../Convolution/BokehBlurProcessor.cs | 12 +++--- 3 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 src/ImageSharp/Primitives/BokehBlurKernelData.cs diff --git a/src/ImageSharp/Primitives/BokehBlurKernelData.cs b/src/ImageSharp/Primitives/BokehBlurKernelData.cs new file mode 100644 index 0000000000..4fe83a489c --- /dev/null +++ b/src/ImageSharp/Primitives/BokehBlurKernelData.cs @@ -0,0 +1,41 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; + +namespace SixLabors.ImageSharp.Primitives +{ + /// + /// A that contains data about a set of bokeh blur kernels + /// + internal readonly struct BokehBlurKernelData + { + /// + /// The kernel parameters to use for the current set of complex kernels + /// + public readonly Vector4[] Parameters; + + /// + /// The scaling factor for the kernel values + /// + public readonly float Scale; + + /// + /// The kernel components to apply the bokeh blur effect + /// + public readonly Complex64[][] Kernels; + + /// + /// Initializes a new instance of the struct. + /// + /// The kernel parameters + /// The kernel scale factor + /// The complex kernel components + public BokehBlurKernelData(Vector4[] parameters, float scale, Complex64[][] kernels) + { + this.Parameters = parameters; + this.Scale = scale; + this.Kernels = kernels; + } + } +} diff --git a/src/ImageSharp/Primitives/BokehBlurParameters.cs b/src/ImageSharp/Primitives/BokehBlurParameters.cs index 1fc2111569..d653115e8e 100644 --- a/src/ImageSharp/Primitives/BokehBlurParameters.cs +++ b/src/ImageSharp/Primitives/BokehBlurParameters.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Primitives /// /// A that contains parameters to apply a bokeh blur filter /// - public readonly struct BokehBlurParameters + internal readonly struct BokehBlurParameters { /// /// The size of the convolution kernel to use when applying the bokeh blur diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 70a3e935c8..4465de077c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -51,7 +51,7 @@ internal class BokehBlurProcessor : ImageProcessor /// /// The mapping of initialized complex kernels and parameters, to speed up the initialization of new instances /// - private static readonly Dictionary Cache = new Dictionary(); + private static readonly Dictionary Cache = new Dictionary(); /// /// Initializes a new instance of the class. @@ -76,11 +76,11 @@ public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) // Reuse the initialized values from the cache, if possible var parameters = new BokehBlurParameters(radius, components); - if (Cache.TryGetValue(parameters, out (Vector4[], float, Complex64[][]) info)) + if (Cache.TryGetValue(parameters, out BokehBlurKernelData info)) { - this.kernelParameters = info.Item1; - this.kernelsScale = info.Item2; - this.kernels = info.Item3; + this.kernelParameters = info.Parameters; + this.kernelsScale = info.Scale; + this.kernels = info.Kernels; } else { @@ -98,7 +98,7 @@ public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) this.NormalizeKernels(); // Store them in the cache for future use - Cache.Add(parameters, (this.kernelParameters, this.kernelsScale, this.kernels)); + Cache.Add(parameters, new BokehBlurKernelData(this.kernelParameters, this.kernelsScale, this.kernels)); } } From 73ee135f4b24eb0ed9e8b01fa51ec29b8cc32432 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 31 Mar 2019 22:45:59 +0200 Subject: [PATCH 51/82] Minor code refactoring --- .../Convolution/BokehBlurProcessor.cs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 4465de077c..57ffc14fec 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -86,15 +86,7 @@ public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) { // Initialize the complex kernels and parameters with the current arguments (this.kernelParameters, this.kernelsScale) = this.GetParameters(); - - this.kernels = new Complex64[this.kernelParameters.Length][]; - ref Vector4 baseRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan()); - for (int i = 0; i < this.kernelParameters.Length; i++) - { - ref Vector4 paramsRef = ref Unsafe.Add(ref baseRef, i); - this.kernels[i] = this.CreateComplex1DKernel(paramsRef.X, paramsRef.Y); - } - + this.kernels = this.CreateComplexKernels(); this.NormalizeKernels(); // Store them in the cache for future use @@ -191,6 +183,22 @@ public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) return (KernelComponents[index], KernelScales[index]); } + /// + /// Creates the collection of complex 1D kernels with the specified parameters + /// + private Complex64[][] CreateComplexKernels() + { + var kernels = new Complex64[this.kernelParameters.Length][]; + ref Vector4 baseRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan()); + for (int i = 0; i < this.kernelParameters.Length; i++) + { + ref Vector4 paramsRef = ref Unsafe.Add(ref baseRef, i); + kernels[i] = this.CreateComplex1DKernel(paramsRef.X, paramsRef.Y); + } + + return kernels; + } + /// /// Creates a complex 1D kernel with the specified parameters /// From de72bfdd10f36bec9042ff075be6a2fb8be13d62 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 8 Apr 2019 00:29:39 +0200 Subject: [PATCH 52/82] Fixed API change build errors --- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 4 ++-- .../Processing/Processors/Convolution/BokehBlurTest.cs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 57ffc14fec..4001ed7642 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -438,7 +438,7 @@ private void ApplyGammaExposure( v.Z = (float)Math.Pow(v.Z, gamma); } - PixelOperations.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan); } }); } @@ -487,7 +487,7 @@ private void ApplyInverseGammaExposure( } Vector4Utils.UnPremultiply(targetRowSpan); - PixelOperations.Instance.FromVector4(configuration, targetRowSpan.Slice(0, width), targetPixelSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, targetRowSpan.Slice(0, width), targetPixelSpan); } }); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index 3215327e6f..e57a21290b 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -11,7 +11,6 @@ using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Convolution; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.Primitives; using Xunit; From 93e8c1a6c69f937c31396847aaad3eb5c7c5cb59 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 8 Apr 2019 01:19:46 +0200 Subject: [PATCH 53/82] Bug fixes with the pixel premultiplication steps --- .../Processors/Convolution/BokehBlurProcessor.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 4001ed7642..875ed3dec2 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -426,8 +426,7 @@ private void ApplyGammaExposure( for (int y = rows.Min; y < rows.Max; y++) { Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); - PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan); - Vector4Utils.Premultiply(vectorSpan); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan, PixelConversionModifiers.Premultiply); ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectorSpan); for (int x = 0; x < width; x++) @@ -438,7 +437,7 @@ private void ApplyGammaExposure( v.Z = (float)Math.Pow(v.Z, gamma); } - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan, PixelConversionModifiers.Premultiply); } }); } @@ -486,8 +485,7 @@ private void ApplyInverseGammaExposure( v.Z = (float)Math.Pow(v.Z.Clamp(0, float.PositiveInfinity), expGamma); } - Vector4Utils.UnPremultiply(targetRowSpan); - PixelOperations.Instance.FromVector4Destructive(configuration, targetRowSpan.Slice(0, width), targetPixelSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, targetRowSpan.Slice(0, width), targetPixelSpan, PixelConversionModifiers.Premultiply); } }); } From 2e89d3d5f387faccfc46c7394f44e8de95ffb4aa Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 8 Apr 2019 01:35:14 +0200 Subject: [PATCH 54/82] Removed unwanted unpremultiplication pass --- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 875ed3dec2..6246030b02 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -437,7 +437,7 @@ private void ApplyGammaExposure( v.Z = (float)Math.Pow(v.Z, gamma); } - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan, PixelConversionModifiers.Premultiply); + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan); } }); } From 7ec69058c0a003d2417db515339e35c3c18ac79b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 8 Apr 2019 01:35:58 +0200 Subject: [PATCH 55/82] Removed unused using directives --- src/ImageSharp.Drawing/Primitives/ShapeRegion.cs | 1 - src/ImageSharp/Common/Extensions/StreamExtensions.cs | 1 - .../Common/Helpers/SimdUtils.BasicIntrinsics256.cs | 1 - src/ImageSharp/Common/Helpers/SimdUtils.cs | 3 --- .../Formats/Jpeg/Components/Decoder/IRawJpegData.cs | 1 - .../Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs | 1 - src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs | 1 - .../PixelImplementations/Rgba32.PixelOperations.cs | 1 - .../PixelFormats/Utils/Vector4Converters.Default.cs | 2 -- .../PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs | 2 -- .../Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs | 2 -- tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs | 1 - .../General/Vectorization/VectorFetching.cs | 1 - .../Advanced/AdvancedImageExtensionsTests.cs | 4 ---- tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs | 3 +-- tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs | 2 +- tests/ImageSharp.Tests/Helpers/GuardTests.cs | 1 - tests/ImageSharp.Tests/Image/ImageSaveTests.cs | 5 +---- tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs | 3 +-- .../PixelBlenders/PorterDuffFunctionsTests_TPixel.cs | 2 -- .../PixelFormats/PixelOperationsTests.Blender.cs | 3 +-- tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs | 1 - tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs | 3 +-- .../Processing/Convolution/DetectEdgesTest.cs | 3 +-- tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs | 2 +- tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs | 3 +-- tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs | 4 +--- tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs | 2 -- .../Processing/Processors/Filters/GrayscaleTest.cs | 2 -- .../Processing/Processors/Transforms/AutoOrientTests.cs | 3 +++ .../Processing/Processors/Transforms/ResizeKernelMapTests.cs | 2 -- .../Processing/Processors/Transforms/RotateFlipTests.cs | 2 +- tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs | 4 +--- tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs | 4 ++++ tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs | 1 - tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs | 1 - tests/ImageSharp.Tests/TestFile.cs | 4 +--- tests/ImageSharp.Tests/TestFormat.cs | 5 ++++- .../TestUtilities/ImageProviders/BlankProvider.cs | 2 -- tests/ImageSharp.Tests/TestUtilities/TestPixel.cs | 3 +-- tests/ImageSharp.Tests/TestUtilities/TestType.cs | 4 ---- .../TestUtilities/Tests/TestEnvironmentTests.cs | 2 -- tests/ImageSharp.Tests/VectorAssert.cs | 2 +- 43 files changed, 26 insertions(+), 74 deletions(-) diff --git a/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs b/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs index 812744b895..f4a6458206 100644 --- a/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs +++ b/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs @@ -5,7 +5,6 @@ using System.Buffers; using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; using SixLabors.Primitives; using SixLabors.Shapes; diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs index 505ecccdda..a7a99ef838 100644 --- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs +++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.IO; using SixLabors.ImageSharp.Memory; diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs index 85c9f00748..b16bbc86b6 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs index 867e7b9de1..45761a0d07 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -7,9 +7,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Tuples; - namespace SixLabors.ImageSharp { /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs index ace8d7215b..2f393fadae 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; using SixLabors.Primitives; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs index 301079b6ae..883a085b5a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs @@ -4,7 +4,6 @@ using System; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder diff --git a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs index c795ccc8b5..3d1e22a99d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs @@ -7,7 +7,6 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Formats.Jpeg.Components diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs index 3da5d1855b..da02f374e1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs @@ -6,7 +6,6 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats.Utils; -using SixLabors.Memory; namespace SixLabors.ImageSharp.PixelFormats { diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs index 4c4e60276d..e67bd9d53e 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs @@ -6,8 +6,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - namespace SixLabors.ImageSharp.PixelFormats.Utils { /// diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs index fe8d7dec3b..0350c669ab 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -7,8 +7,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - namespace SixLabors.ImageSharp.PixelFormats.Utils { /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs index 4b81aaa64e..6d6e22a6a5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; - using SixLabors.Memory; namespace SixLabors.ImageSharp.Processing.Processors.Transforms diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs index 3b7dea0955..404714a54b 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs @@ -2,7 +2,6 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath { diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs index 017f58ef74..f2dab02ef5 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs @@ -5,7 +5,6 @@ namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; - using SixLabors.Memory; /// /// This benchmark compares different methods for fetching memory data into diff --git a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs index 974099991d..4b27b7b2da 100644 --- a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs +++ b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs @@ -10,10 +10,6 @@ namespace SixLabors.ImageSharp.Tests.Advanced { - using System.Buffers; - - using SixLabors.Memory; - public class AdvancedImageExtensionsTests { public class GetPixelMemory diff --git a/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs index 894d902b78..64a394cc98 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Buffers.Binary; +using System.Buffers.Binary; using System.Text; using SixLabors.ImageSharp.Formats.Png; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index 81a31e42d3..b1408247cf 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -3,7 +3,7 @@ using System.IO; using Xunit; -using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; diff --git a/tests/ImageSharp.Tests/Helpers/GuardTests.cs b/tests/ImageSharp.Tests/Helpers/GuardTests.cs index b847e581f5..6bccea2c3e 100644 --- a/tests/ImageSharp.Tests/Helpers/GuardTests.cs +++ b/tests/ImageSharp.Tests/Helpers/GuardTests.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.Linq; using Xunit; // ReSharper disable InconsistentNaming diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs index 3f4cb8afa2..c4be6fbecf 100644 --- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs @@ -3,7 +3,7 @@ using System; using System.IO; -using System.Linq; + using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.PixelFormats; @@ -13,9 +13,6 @@ namespace SixLabors.ImageSharp.Tests { - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - /// /// Tests the class. /// diff --git a/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs b/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs index 9d709d488b..975ed84e0b 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs @@ -4,8 +4,7 @@ using System; using System.IO; using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.IO; -using Moq; + using Xunit; // ReSharper disable InconsistentNaming diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs index 7de1cbb190..9ca1e30cce 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -9,8 +9,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { - using SixLabors.Memory; - public class PorterDuffFunctionsTestsTPixel { private static Span AsSpan(T value) diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs index a044ebae9b..eea0b5eac5 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs @@ -2,8 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.Text; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.ImageSharp.Tests.TestUtilities; diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs index 275afa35d6..429a655834 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs @@ -5,7 +5,6 @@ using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using Xunit; -using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests.PixelFormats { diff --git a/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs index 2fbe260ecd..ce49f04da5 100644 --- a/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs +++ b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Globalization; +using System.Globalization; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs index 07b9b1b8c6..31699fc49d 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -4,10 +4,9 @@ using System.Collections.Generic; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.ImageSharp.Tests.TestUtilities; -using SixLabors.Primitives; + using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Convolution diff --git a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs index ff2b1c702d..d3fc424c9e 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; + using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Effects diff --git a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs index d598f0ac88..73558a64a8 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs @@ -2,8 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors; + using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Filters diff --git a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs index 446cfa1c33..dde419bce7 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs @@ -1,10 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.IO; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Primitives; + using Xunit; namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs index 0301f5c039..978fd416bc 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; - using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs index f08ec147ec..c2728e0435 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs @@ -1,9 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs index 6dc9b36309..a6fa5c8f48 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs @@ -5,8 +5,11 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +<<<<<<< HEAD using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +======= +>>>>>>> Removed unused using directives using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs index 51680eee04..dc169df816 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs @@ -2,9 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Text; using SixLabors.ImageSharp.Processing; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs index 20e12cb7f9..1e08836c13 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; + using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms diff --git a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs index 9fe2977adb..ccfe8e4c00 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs @@ -1,10 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors; + using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index b870ddd08a..3e0c68e8b6 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -1,8 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +<<<<<<< HEAD using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; +======= +using SixLabors.ImageSharp.PixelFormats; +>>>>>>> Removed unused using directives using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs index 38033e80d0..e9a6120ca8 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs index 1fb745c38f..799794ca4f 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Tests { diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index 6ca86ced6e..8219920909 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -3,10 +3,8 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Reflection; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 23bd0a54c3..78380a015f 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -2,12 +2,15 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; +<<<<<<< HEAD using System.Numerics; using System.Reflection; +======= + +>>>>>>> Removed unused using directives using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs index dae2f0cfe4..0860af1a4e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; - using SixLabors.ImageSharp.PixelFormats; using Xunit.Abstractions; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs index e998ccd3dc..1e1a45f074 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs @@ -2,8 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.Text; + using SixLabors.ImageSharp.PixelFormats; using Xunit.Abstractions; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestType.cs b/tests/ImageSharp.Tests/TestUtilities/TestType.cs index 852aaf2d43..7643fb321e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestType.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestType.cs @@ -1,10 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; -using System.Text; -using SixLabors.ImageSharp.PixelFormats; using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests.TestUtilities diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs index 122234ae89..096f78299b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs @@ -3,8 +3,6 @@ using System; using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats; diff --git a/tests/ImageSharp.Tests/VectorAssert.cs b/tests/ImageSharp.Tests/VectorAssert.cs index 402d066555..9612882b3d 100644 --- a/tests/ImageSharp.Tests/VectorAssert.cs +++ b/tests/ImageSharp.Tests/VectorAssert.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Numerics; -using SixLabors.ImageSharp; + using SixLabors.ImageSharp.PixelFormats; using Xunit; From 2c5c85dc542df52234a7d4ab990cc172b65b98c3 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 8 Apr 2019 01:53:23 +0200 Subject: [PATCH 56/82] Fixed missing using directives in conditional branches --- src/ImageSharp/Common/Extensions/StreamExtensions.cs | 3 +++ tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs index a7a99ef838..3673b8f310 100644 --- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs +++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs @@ -2,6 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System; +#if NET472 || NETSTANDARD1_3 || NETSTANDARD2_0 +using System.Buffers; +#endif using System.IO; using SixLabors.ImageSharp.Memory; diff --git a/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs index ce49f04da5..2fbe260ecd 100644 --- a/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs +++ b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs @@ -1,4 +1,5 @@ -using System.Globalization; +using System; +using System.Globalization; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using Xunit; From 343da6682166a40960bac3bff930683402057506 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 Jul 2019 13:37:53 +1000 Subject: [PATCH 57/82] Update from latest upstream master --- .../Primitives/ShapeRegion.cs | 1 + .../Common/Extensions/StreamExtensions.cs | 2 - .../Common/Helpers/DenseMatrixUtils.cs | 2 +- .../Helpers/SimdUtils.BasicIntrinsics256.cs | 1 + src/ImageSharp/Common/Helpers/SimdUtils.cs | 3 + .../Jpeg/Components/Block8x8F.Generated.cs | 22 +- .../Jpeg/Components/Decoder/IRawJpegData.cs | 1 + .../Encoder/YCbCrForwardConverter{TPixel}.cs | 1 + .../Components/GenericBlock8x8.Generated.cs | 10 +- .../Jpeg/Components/GenericBlock8x8.cs | 1 + .../Rgba32.PixelOperations.cs | 1 + .../PixelFormats/PixelOperations{TPixel}.cs | 1 + .../Utils/Vector4Converters.Default.cs | 2 + .../Utils/Vector4Converters.RgbaCompatible.cs | 2 + src/ImageSharp/Primitives/Rational.cs | 2 +- .../{ => Extensions}/BokehBlurExtensions.cs | 35 +- .../Convolution/BokehBlurProcessor.cs | 512 +---------------- .../Convolution/BokehBlurProcessor{TPixel}.cs | 531 ++++++++++++++++++ .../ResizeKernelMap.PeriodicKernelMap.cs | 2 + .../General/BasicMath/ClampFloat.cs | 1 + .../General/Vectorization/VectorFetching.cs | 1 + .../Advanced/AdvancedImageExtensionsTests.cs | 4 + .../Formats/Png/PngChunkTypeTests.cs | 3 +- .../Formats/Png/PngSmokeTests.cs | 2 +- tests/ImageSharp.Tests/Helpers/GuardTests.cs | 1 + .../ImageSharp.Tests/Image/ImageSaveTests.cs | 5 +- .../Image/ImageTests.DetectFormat.cs | 3 +- .../PorterDuffFunctionsTests_TPixel.cs | 2 + .../PixelOperationsTests.Blender.cs | 3 +- .../PixelFormats/Rgba32Tests.cs | 1 + .../Processing/Convolution/DetectEdgesTest.cs | 3 +- .../Processing/Filters/ContrastTest.cs | 2 +- .../Processing/Filters/FilterTest.cs | 3 +- .../Processing/Filters/LomographTest.cs | 4 +- .../Processing/Overlays/GlowTest.cs | 2 + .../Processors/Convolution/BokehBlurTest.cs | 5 +- .../Processors/Filters/GrayscaleTest.cs | 2 + .../Processors/Transforms/AutoOrientTests.cs | 3 - .../Transforms/ResizeKernelMapTests.cs | 2 + .../Processors/Transforms/RotateFlipTests.cs | 2 +- .../Processing/Transforms/FlipTests.cs | 4 +- .../Processing/Transforms/PadTest.cs | 4 - .../Processing/Transforms/SkewTest.cs | 1 + .../TestDataIcc/IccTestDataMatrix.cs | 1 + tests/ImageSharp.Tests/TestFile.cs | 4 +- tests/ImageSharp.Tests/TestFormat.cs | 5 +- .../ImageProviders/BlankProvider.cs | 2 + .../TestUtilities/TestPixel.cs | 3 +- .../TestUtilities/TestType.cs | 4 + .../Tests/TestEnvironmentTests.cs | 2 + tests/ImageSharp.Tests/VectorAssert.cs | 2 +- 51 files changed, 676 insertions(+), 542 deletions(-) rename src/ImageSharp/Processing/{ => Extensions}/BokehBlurExtensions.cs (54%) create mode 100644 src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs diff --git a/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs b/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs index f4a6458206..812744b895 100644 --- a/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs +++ b/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs @@ -5,6 +5,7 @@ using System.Buffers; using SixLabors.ImageSharp.Memory; +using SixLabors.Memory; using SixLabors.Primitives; using SixLabors.Shapes; diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs index 3673b8f310..505ecccdda 100644 --- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs +++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs @@ -2,9 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -#if NET472 || NETSTANDARD1_3 || NETSTANDARD2_0 using System.Buffers; -#endif using System.IO; using SixLabors.ImageSharp.Memory; diff --git a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs index b97651dbfa..75795fca46 100644 --- a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs +++ b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs index b16bbc86b6..85c9f00748 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs index 45761a0d07..867e7b9de1 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -7,6 +7,9 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tuples; + namespace SixLabors.ImageSharp { /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs index a9e9903a9d..b132ce7a0c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs @@ -1,4 +1,11 @@ -// Copyright (c) Six Labors and contributors. + + + + + + + +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -6,6 +13,7 @@ using System.Runtime.CompilerServices; // + namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal partial struct Block8x8F @@ -88,6 +96,7 @@ public void TransposeInto(ref Block8x8F d) d.V5R.W = V7R.Y; d.V6R.W = V7R.Z; d.V7R.W = V7R.W; + } /// @@ -115,6 +124,7 @@ public void NormalizeColorsInplace(float maximum) this.V6R = Vector4.Clamp(this.V6R + COff4, CMin4, CMax4); this.V7L = Vector4.Clamp(this.V7L + COff4, CMin4, CMax4); this.V7R = Vector4.Clamp(this.V7R + COff4, CMin4, CMax4); + } /// @@ -126,30 +136,39 @@ public void NormalizeColorsAndRoundInplaceAvx2(float maximum) Vector off = new Vector(MathF.Ceiling(maximum / 2)); Vector max = new Vector(maximum); + ref Vector row0 = ref Unsafe.As>(ref this.V0L); row0 = NormalizeAndRound(row0, off, max); + ref Vector row1 = ref Unsafe.As>(ref this.V1L); row1 = NormalizeAndRound(row1, off, max); + ref Vector row2 = ref Unsafe.As>(ref this.V2L); row2 = NormalizeAndRound(row2, off, max); + ref Vector row3 = ref Unsafe.As>(ref this.V3L); row3 = NormalizeAndRound(row3, off, max); + ref Vector row4 = ref Unsafe.As>(ref this.V4L); row4 = NormalizeAndRound(row4, off, max); + ref Vector row5 = ref Unsafe.As>(ref this.V5L); row5 = NormalizeAndRound(row5, off, max); + ref Vector row6 = ref Unsafe.As>(ref this.V6L); row6 = NormalizeAndRound(row6, off, max); + ref Vector row7 = ref Unsafe.As>(ref this.V7L); row7 = NormalizeAndRound(row7, off, max); + } /// @@ -230,6 +249,7 @@ public void LoadFromInt16Scalar(ref Block8x8 source) this.V7R.Y = Unsafe.Add(ref selfRef, 61); this.V7R.Z = Unsafe.Add(ref selfRef, 62); this.V7R.W = Unsafe.Add(ref selfRef, 63); + } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs index 2f393fadae..ace8d7215b 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Collections.Generic; using SixLabors.Primitives; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs index 883a085b5a..301079b6ae 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder diff --git a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs index 0cc729371f..ac0e6a6d26 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs @@ -1,4 +1,11 @@ -// Copyright (c) Six Labors and contributors. + + + + + + + +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. // @@ -18,6 +25,7 @@ internal unsafe partial struct GenericBlock8x8 private T _y6_x0, _y6_x1, _y6_x2, _y6_x3, _y6_x4, _y6_x5, _y6_x6, _y6_x7; private T _y7_x0, _y7_x1, _y7_x2, _y7_x3, _y7_x4, _y7_x5, _y7_x6, _y7_x7; + #pragma warning restore 169 } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs index 3d1e22a99d..c795ccc8b5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Formats.Jpeg.Components diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs index da02f374e1..3da5d1855b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats.Utils; +using SixLabors.Memory; namespace SixLabors.ImageSharp.PixelFormats { diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index d7c1b8b6a0..2b2d79c732 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs index e67bd9d53e..4c4e60276d 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.ColorSpaces.Companding; + namespace SixLabors.ImageSharp.PixelFormats.Utils { /// diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs index 0350c669ab..fe8d7dec3b 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -7,6 +7,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.ColorSpaces.Companding; + namespace SixLabors.ImageSharp.PixelFormats.Utils { /// diff --git a/src/ImageSharp/Primitives/Rational.cs b/src/ImageSharp/Primitives/Rational.cs index 6b134bbfd7..b598f0e02f 100644 --- a/src/ImageSharp/Primitives/Rational.cs +++ b/src/ImageSharp/Primitives/Rational.cs @@ -102,7 +102,7 @@ public Rational(double value, bool bestPrecision) /// Determines whether the specified instances are not considered equal. /// /// The first to compare. - /// The second to compare. + /// The second to compare. /// The public static bool operator !=(Rational left, Rational right) { diff --git a/src/ImageSharp/Processing/BokehBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs similarity index 54% rename from src/ImageSharp/Processing/BokehBlurExtensions.cs rename to src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs index 62c049d8bc..2bbdd03b05 100644 --- a/src/ImageSharp/Processing/BokehBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs @@ -1,7 +1,6 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.Primitives; @@ -15,43 +14,36 @@ public static class BokehBlurExtensions /// /// Applies a bokeh blur to the image. /// - /// The pixel format. /// The image this method extends. - /// The . - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BokehBlurProcessor()); + /// The to allow chaining of operations. + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source) + => source.ApplyProcessor(new BokehBlurProcessor()); /// /// Applies a bokeh blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. /// The gamma highlight factor to use to emphasize bright spots in the source image - /// The . - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma)); + /// The to allow chaining of operations. + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma) + => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma)); /// /// Applies a bokeh blur to the image. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BokehBlurProcessor(), rectangle); + /// The to allow chaining of operations. + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle) + => source.ApplyProcessor(new BokehBlurProcessor(), rectangle); /// /// Applies a bokeh blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. @@ -59,9 +51,8 @@ public static IImageProcessingContext BokehBlur(this IImageProce /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), rectangle); + /// The to allow chaining of operations. + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle) + => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), rectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 6246030b02..b6a65821c6 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -1,60 +1,40 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// /// Applies bokeh blur processing to the image. /// - /// The pixel format. - internal class BokehBlurProcessor : ImageProcessor - where TPixel : struct, IPixel + public sealed class BokehBlurProcessor : IImageProcessor { /// - /// The maximum size of the kernel in either direction. - /// - private readonly int kernelSize; - - /// - /// The number of components to use when applying the bokeh blur - /// - private readonly int componentsCount; - - /// - /// The kernel parameters to use for the current instance (a: X, b: Y, A: Z, B: W) + /// The default radius used by the parameterless constructor. /// - private readonly Vector4[] kernelParameters; + public const int DefaultRadius = 32; /// - /// The kernel components for the current instance + /// The default component count used by the parameterless constructor. /// - private readonly Complex64[][] kernels; + public const int DefaultComponents = 2; /// - /// The scaling factor for kernel values + /// The default gamma used by the parameterless constructor. /// - private readonly float kernelsScale; + public const float DefaultGamma = 3F; /// - /// The mapping of initialized complex kernels and parameters, to speed up the initialization of new instances + /// Initializes a new instance of the class. /// - private static readonly Dictionary Cache = new Dictionary(); + public BokehBlurProcessor() + : this(DefaultRadius, DefaultComponents, DefaultGamma) + { + } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. @@ -65,477 +45,35 @@ internal class BokehBlurProcessor : ImageProcessor /// /// The gamma highlight factor to use to further process the image. /// - public BokehBlurProcessor(int radius = 32, int components = 2, float gamma = 3) + public BokehBlurProcessor(int radius, int components, float gamma) { - SixLabors.Guard.MustBeGreaterThanOrEqualTo(gamma, 1, nameof(gamma)); + Guard.MustBeGreaterThanOrEqualTo(gamma, 1, nameof(gamma)); this.Radius = radius; - this.kernelSize = (radius * 2) + 1; - this.componentsCount = components; + this.Components = components; this.Gamma = gamma; - - // Reuse the initialized values from the cache, if possible - var parameters = new BokehBlurParameters(radius, components); - if (Cache.TryGetValue(parameters, out BokehBlurKernelData info)) - { - this.kernelParameters = info.Parameters; - this.kernelsScale = info.Scale; - this.kernels = info.Kernels; - } - else - { - // Initialize the complex kernels and parameters with the current arguments - (this.kernelParameters, this.kernelsScale) = this.GetParameters(); - this.kernels = this.CreateComplexKernels(); - this.NormalizeKernels(); - - // Store them in the cache for future use - Cache.Add(parameters, new BokehBlurKernelData(this.kernelParameters, this.kernelsScale, this.kernels)); - } } /// - /// Gets the Radius + /// Gets the radius. /// public int Radius { get; } /// - /// Gets the complex kernels to use to apply the blur for the current instance - /// - public IReadOnlyList Kernels => this.kernels; - - /// - /// Gets the kernel parameters used to compute the pixel values from each complex pixel + /// Gets the number of components. /// - public IReadOnlyList KernelParameters => this.kernelParameters; + public int Components { get; } /// - /// Gets the gamma highlight factor to use when applying the effect + /// Gets the gamma highlight factor to use when applying the effect. /// public float Gamma { get; } - /// - /// Gets the kernel scales to adjust the component values in each kernel - /// - private static IReadOnlyList KernelScales { get; } = new[] { 1.4f, 1.2f, 1.2f, 1.2f, 1.2f, 1.2f }; - - /// - /// Gets the available bokeh blur kernel parameters - /// - private static IReadOnlyList KernelComponents { get; } = new[] + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - // 1 component - new[] { new Vector4(0.862325f, 1.624835f, 0.767583f, 1.862321f) }, - - // 2 components - new[] - { - new Vector4(0.886528f, 5.268909f, 0.411259f, -0.548794f), - new Vector4(1.960518f, 1.558213f, 0.513282f, 4.56111f) - }, - - // 3 components - new[] - { - new Vector4(2.17649f, 5.043495f, 1.621035f, -2.105439f), - new Vector4(1.019306f, 9.027613f, -0.28086f, -0.162882f), - new Vector4(2.81511f, 1.597273f, -0.366471f, 10.300301f) - }, - - // 4 components - new[] - { - new Vector4(4.338459f, 1.553635f, -5.767909f, 46.164397f), - new Vector4(3.839993f, 4.693183f, 9.795391f, -15.227561f), - new Vector4(2.791880f, 8.178137f, -3.048324f, 0.302959f), - new Vector4(1.342190f, 12.328289f, 0.010001f, 0.244650f) - }, - - // 5 components - new[] - { - new Vector4(4.892608f, 1.685979f, -22.356787f, 85.91246f), - new Vector4(4.71187f, 4.998496f, 35.918936f, -28.875618f), - new Vector4(4.052795f, 8.244168f, -13.212253f, -1.578428f), - new Vector4(2.929212f, 11.900859f, 0.507991f, 1.816328f), - new Vector4(1.512961f, 16.116382f, 0.138051f, -0.01f) - }, - - // 6 components - new[] - { - new Vector4(5.143778f, 2.079813f, -82.326596f, 111.231024f), - new Vector4(5.612426f, 6.153387f, 113.878661f, 58.004879f), - new Vector4(5.982921f, 9.802895f, 39.479083f, -162.028887f), - new Vector4(6.505167f, 11.059237f, -71.286026f, 95.027069f), - new Vector4(3.869579f, 14.81052f, 1.405746f, -3.704914f), - new Vector4(2.201904f, 19.032909f, -0.152784f, -0.107988f) - } - }; - - /// - /// Gets the kernel parameters and scaling factor for the current count value in the current instance - /// - private (Vector4[] Parameters, float Scale) GetParameters() - { - // Prepare the kernel components - int index = Math.Max(0, Math.Min(this.componentsCount - 1, KernelComponents.Count)); - return (KernelComponents[index], KernelScales[index]); - } - - /// - /// Creates the collection of complex 1D kernels with the specified parameters - /// - private Complex64[][] CreateComplexKernels() - { - var kernels = new Complex64[this.kernelParameters.Length][]; - ref Vector4 baseRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan()); - for (int i = 0; i < this.kernelParameters.Length; i++) - { - ref Vector4 paramsRef = ref Unsafe.Add(ref baseRef, i); - kernels[i] = this.CreateComplex1DKernel(paramsRef.X, paramsRef.Y); - } - - return kernels; - } - - /// - /// Creates a complex 1D kernel with the specified parameters - /// - /// The exponential parameter for each complex component - /// The angle component for each complex component - private Complex64[] CreateComplex1DKernel(float a, float b) - { - var kernel = new Complex64[this.kernelSize]; - ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel.AsSpan()); - int r = this.Radius, n = -r; - - for (int i = 0; i < this.kernelSize; i++, n++) - { - // Incrementally compute the range values - float value = n * this.kernelsScale * (1f / r); - value *= value; - - // Fill in the complex kernel values - Unsafe.Add(ref baseRef, i) = new Complex64( - (float)(Math.Exp(-a * value) * Math.Cos(b * value)), - (float)(Math.Exp(-a * value) * Math.Sin(b * value))); - } - - return kernel; - } - - /// - /// Normalizes the kernels with respect to A * real + B * imaginary - /// - private void NormalizeKernels() - { - // Calculate the complex weighted sum - double total = 0; - Span kernelsSpan = this.kernels.AsSpan(); - ref Complex64[] baseKernelsRef = ref MemoryMarshal.GetReference(kernelsSpan); - ref Vector4 baseParamsRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan()); - - for (int i = 0; i < this.kernelParameters.Length; i++) - { - ref Complex64[] kernelRef = ref Unsafe.Add(ref baseKernelsRef, i); - int length = kernelRef.Length; - ref Complex64 valueRef = ref kernelRef[0]; - ref Vector4 paramsRef = ref Unsafe.Add(ref baseParamsRef, i); - - for (int j = 0; j < length; j++) - { - for (int k = 0; k < length; k++) - { - ref Complex64 jRef = ref Unsafe.Add(ref valueRef, j); - ref Complex64 kRef = ref Unsafe.Add(ref valueRef, k); - total += - (paramsRef.Z * ((jRef.Real * kRef.Real) - (jRef.Imaginary * kRef.Imaginary))) + - (paramsRef.W * ((jRef.Real * kRef.Imaginary) + (jRef.Imaginary * kRef.Real))); - } - } - } - - // Normalize the kernels - float scalar = (float)(1f / Math.Sqrt(total)); - for (int i = 0; i < kernelsSpan.Length; i++) - { - ref Complex64[] kernelsRef = ref Unsafe.Add(ref baseKernelsRef, i); - int length = kernelsRef.Length; - ref Complex64 valueRef = ref kernelsRef[0]; - - for (int j = 0; j < length; j++) - { - Unsafe.Add(ref valueRef, j) *= scalar; - } - } - } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - // Preliminary gamma highlight pass - this.ApplyGammaExposure(source.PixelBuffer, sourceRectangle, configuration); - - // Create a 0-filled buffer to use to store the result of the component convolutions - using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean)) - using (Buffer2D - partialValues = configuration.MemoryAllocator.Allocate2D(source.Size()), - firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) - { - // Perform two 1D convolutions for each component in the current instance - ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan()); - for (int i = 0; i < this.kernels.Length; i++) - { - // Compute the resulting complex buffer for the current component - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - Complex64[] kernel = Unsafe.Add(ref baseRef, i); - this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); - this.ApplyConvolution(partialValues, firstPassValues, interest, kernel, configuration); - - // Add the results of the convolution with the current kernel - Vector4 parameters = this.kernelParameters[i]; - this.SumProcessingPartials(processing, partialValues, sourceRectangle, configuration, parameters.Z, parameters.W); - } - - // Apply the inverse gamma exposure pass, and write the final pixel data - this.ApplyInverseGammaExposure(source.PixelBuffer, processing, sourceRectangle, configuration); - } - } - - /// - /// Applies the process to the specified portion of the specified at the specified location - /// and with the specified size. - /// - /// The target values to use to store the results. - /// The source pixels. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - /// The 1D kernel. - /// The - private void ApplyConvolution( - Buffer2D targetValues, - Buffer2D sourcePixels, - Rectangle sourceRectangle, - Complex64[] kernel, - Configuration configuration) - { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - int maxY = endY - 1; - int maxX = endX - 1; - - var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - int width = workingRectangle.Width; - - ParallelHelper.IterateRows( - workingRectangle, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); - - for (int x = 0; x < width; x++) - { - DenseMatrixUtils.Convolve1D(kernel, sourcePixels, targetRowSpan, y, x, startY, maxY, startX, maxX); - } - } - }); - } - - /// - /// Applies the process to the specified portion of the specified buffer at the specified location - /// and with the specified size. - /// - /// The target values to use to store the results. - /// The source complex values. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - /// The 1D kernel. - /// The - private void ApplyConvolution( - Buffer2D targetValues, - Buffer2D sourceValues, - Rectangle sourceRectangle, - Complex64[] kernel, - Configuration configuration) - { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - int maxY = endY - 1; - int maxX = endX - 1; - - var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - int width = workingRectangle.Width; - - ParallelHelper.IterateRows( - workingRectangle, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); - - for (int x = 0; x < width; x++) - { - DenseMatrixUtils.Convolve1D(kernel, sourceValues, targetRowSpan, y, x, startY, maxY, startX, maxX); - } - } - }); - } - - /// - /// Applies the gamma correction/highlight to the input pixel buffer. - /// - /// The target pixel buffer to adjust. - /// - /// The structure that specifies the portion of the image object to draw. - /// - /// The - private void ApplyGammaExposure( - Buffer2D targetPixels, - Rectangle sourceRectangle, - Configuration configuration) - { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - int width = workingRectangle.Width; - float gamma = this.Gamma; - - ParallelHelper.IterateRowsWithTempBuffer( - workingRectangle, - configuration, - (rows, vectorBuffer) => - { - Span vectorSpan = vectorBuffer.Span; - int length = vectorSpan.Length; - - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); - PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan, PixelConversionModifiers.Premultiply); - ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectorSpan); - - for (int x = 0; x < width; x++) - { - ref Vector4 v = ref Unsafe.Add(ref baseRef, x); - v.X = (float)Math.Pow(v.X, gamma); - v.Y = (float)Math.Pow(v.Y, gamma); - v.Z = (float)Math.Pow(v.Z, gamma); - } - - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan); - } - }); - } - - /// - /// Applies the inverse gamma correction/highlight pass, and converts the input buffer into pixel values. - /// - /// The target pixels to apply the process to. - /// The source values. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - /// The - private void ApplyInverseGammaExposure( - Buffer2D targetPixels, - Buffer2D sourceValues, - Rectangle sourceRectangle, - Configuration configuration) - { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - int width = workingRectangle.Width; - float expGamma = 1 / this.Gamma; - - ParallelHelper.IterateRows( - workingRectangle, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = sourceValues.GetRowSpan(y).Slice(startX); - Span targetPixelSpan = targetPixels.GetRowSpan(y).Slice(startX); - ref Vector4 baseRef = ref MemoryMarshal.GetReference(targetRowSpan); - - for (int x = 0; x < width; x++) - { - ref Vector4 v = ref Unsafe.Add(ref baseRef, x); - v.X = (float)Math.Pow(v.X.Clamp(0, float.PositiveInfinity), expGamma); - v.Y = (float)Math.Pow(v.Y.Clamp(0, float.PositiveInfinity), expGamma); - v.Z = (float)Math.Pow(v.Z.Clamp(0, float.PositiveInfinity), expGamma); - } - - PixelOperations.Instance.FromVector4Destructive(configuration, targetRowSpan.Slice(0, width), targetPixelSpan, PixelConversionModifiers.Premultiply); - } - }); - } - - /// - /// Applies the process to the specified portion of the specified at the specified location - /// and with the specified size. - /// - /// The target instance to use to store the results. - /// The source complex pixels. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - /// The - /// The weight factor for the real component of the complex pixel values. - /// The weight factor for the imaginary component of the complex pixel values. - private void SumProcessingPartials( - Buffer2D targetValues, - Buffer2D sourceValues, - Rectangle sourceRectangle, - Configuration configuration, - float z, - float w) - { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - int width = workingRectangle.Width; - - ParallelHelper.IterateRows( - workingRectangle, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); - Span sourceRowSpan = sourceValues.GetRowSpan(y).Slice(startX); - ref Vector4 baseTargetRef = ref MemoryMarshal.GetReference(targetRowSpan); - ref ComplexVector4 baseSourceRef = ref MemoryMarshal.GetReference(sourceRowSpan); - - for (int x = 0; x < width; x++) - { - Unsafe.Add(ref baseTargetRef, x) += Unsafe.Add(ref baseSourceRef, x).WeightedSum(z, w); - } - } - }); + return new BokehBlurProcessor(this); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs new file mode 100644 index 0000000000..b6204e327e --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -0,0 +1,531 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Applies bokeh blur processing to the image. + /// + /// The pixel format. + internal class BokehBlurProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// The kernel radius. + /// + private readonly int radius; + + /// + /// The gamma highlight factor to use when applying the effect + /// + private readonly float gamma; + + /// + /// The maximum size of the kernel in either direction. + /// + private readonly int kernelSize; + + /// + /// The number of components to use when applying the bokeh blur + /// + private readonly int componentsCount; + + /// + /// The kernel parameters to use for the current instance (a: X, b: Y, A: Z, B: W) + /// + private readonly Vector4[] kernelParameters; + + /// + /// The kernel components for the current instance + /// + private readonly Complex64[][] kernels; + + /// + /// The scaling factor for kernel values + /// + private readonly float kernelsScale; + + /// + /// The mapping of initialized complex kernels and parameters, to speed up the initialization of new instances + /// + private static readonly Dictionary Cache = new Dictionary(); + + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + public BokehBlurProcessor(BokehBlurProcessor definition) + { + this.radius = definition.Radius; + this.kernelSize = (this.radius * 2) + 1; + this.componentsCount = definition.Components; + this.gamma = definition.Gamma; + + // Reuse the initialized values from the cache, if possible + var parameters = new BokehBlurParameters(this.radius, this.componentsCount); + if (Cache.TryGetValue(parameters, out BokehBlurKernelData info)) + { + this.kernelParameters = info.Parameters; + this.kernelsScale = info.Scale; + this.kernels = info.Kernels; + } + else + { + // Initialize the complex kernels and parameters with the current arguments + (this.kernelParameters, this.kernelsScale) = this.GetParameters(); + this.kernels = this.CreateComplexKernels(); + this.NormalizeKernels(); + + // Store them in the cache for future use + Cache.Add(parameters, new BokehBlurKernelData(this.kernelParameters, this.kernelsScale, this.kernels)); + } + } + + /// + /// Gets the complex kernels to use to apply the blur for the current instance + /// + public IReadOnlyList Kernels => this.kernels; + + /// + /// Gets the kernel parameters used to compute the pixel values from each complex pixel + /// + public IReadOnlyList KernelParameters => this.kernelParameters; + + /// + /// Gets the kernel scales to adjust the component values in each kernel + /// + private static IReadOnlyList KernelScales { get; } = new[] { 1.4f, 1.2f, 1.2f, 1.2f, 1.2f, 1.2f }; + + /// + /// Gets the available bokeh blur kernel parameters + /// + private static IReadOnlyList KernelComponents { get; } = new[] + { + // 1 component + new[] { new Vector4(0.862325f, 1.624835f, 0.767583f, 1.862321f) }, + + // 2 components + new[] + { + new Vector4(0.886528f, 5.268909f, 0.411259f, -0.548794f), + new Vector4(1.960518f, 1.558213f, 0.513282f, 4.56111f) + }, + + // 3 components + new[] + { + new Vector4(2.17649f, 5.043495f, 1.621035f, -2.105439f), + new Vector4(1.019306f, 9.027613f, -0.28086f, -0.162882f), + new Vector4(2.81511f, 1.597273f, -0.366471f, 10.300301f) + }, + + // 4 components + new[] + { + new Vector4(4.338459f, 1.553635f, -5.767909f, 46.164397f), + new Vector4(3.839993f, 4.693183f, 9.795391f, -15.227561f), + new Vector4(2.791880f, 8.178137f, -3.048324f, 0.302959f), + new Vector4(1.342190f, 12.328289f, 0.010001f, 0.244650f) + }, + + // 5 components + new[] + { + new Vector4(4.892608f, 1.685979f, -22.356787f, 85.91246f), + new Vector4(4.71187f, 4.998496f, 35.918936f, -28.875618f), + new Vector4(4.052795f, 8.244168f, -13.212253f, -1.578428f), + new Vector4(2.929212f, 11.900859f, 0.507991f, 1.816328f), + new Vector4(1.512961f, 16.116382f, 0.138051f, -0.01f) + }, + + // 6 components + new[] + { + new Vector4(5.143778f, 2.079813f, -82.326596f, 111.231024f), + new Vector4(5.612426f, 6.153387f, 113.878661f, 58.004879f), + new Vector4(5.982921f, 9.802895f, 39.479083f, -162.028887f), + new Vector4(6.505167f, 11.059237f, -71.286026f, 95.027069f), + new Vector4(3.869579f, 14.81052f, 1.405746f, -3.704914f), + new Vector4(2.201904f, 19.032909f, -0.152784f, -0.107988f) + } + }; + + /// + /// Gets the kernel parameters and scaling factor for the current count value in the current instance + /// + private (Vector4[] Parameters, float Scale) GetParameters() + { + // Prepare the kernel components + int index = Math.Max(0, Math.Min(this.componentsCount - 1, KernelComponents.Count)); + return (KernelComponents[index], KernelScales[index]); + } + + /// + /// Creates the collection of complex 1D kernels with the specified parameters + /// + private Complex64[][] CreateComplexKernels() + { + var kernels = new Complex64[this.kernelParameters.Length][]; + ref Vector4 baseRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan()); + for (int i = 0; i < this.kernelParameters.Length; i++) + { + ref Vector4 paramsRef = ref Unsafe.Add(ref baseRef, i); + kernels[i] = this.CreateComplex1DKernel(paramsRef.X, paramsRef.Y); + } + + return kernels; + } + + /// + /// Creates a complex 1D kernel with the specified parameters + /// + /// The exponential parameter for each complex component + /// The angle component for each complex component + private Complex64[] CreateComplex1DKernel(float a, float b) + { + var kernel = new Complex64[this.kernelSize]; + ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel.AsSpan()); + int r = this.radius, n = -r; + + for (int i = 0; i < this.kernelSize; i++, n++) + { + // Incrementally compute the range values + float value = n * this.kernelsScale * (1f / r); + value *= value; + + // Fill in the complex kernel values + Unsafe.Add(ref baseRef, i) = new Complex64( + (float)(Math.Exp(-a * value) * Math.Cos(b * value)), + (float)(Math.Exp(-a * value) * Math.Sin(b * value))); + } + + return kernel; + } + + /// + /// Normalizes the kernels with respect to A * real + B * imaginary + /// + private void NormalizeKernels() + { + // Calculate the complex weighted sum + double total = 0; + Span kernelsSpan = this.kernels.AsSpan(); + ref Complex64[] baseKernelsRef = ref MemoryMarshal.GetReference(kernelsSpan); + ref Vector4 baseParamsRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan()); + + for (int i = 0; i < this.kernelParameters.Length; i++) + { + ref Complex64[] kernelRef = ref Unsafe.Add(ref baseKernelsRef, i); + int length = kernelRef.Length; + ref Complex64 valueRef = ref kernelRef[0]; + ref Vector4 paramsRef = ref Unsafe.Add(ref baseParamsRef, i); + + for (int j = 0; j < length; j++) + { + for (int k = 0; k < length; k++) + { + ref Complex64 jRef = ref Unsafe.Add(ref valueRef, j); + ref Complex64 kRef = ref Unsafe.Add(ref valueRef, k); + total += + (paramsRef.Z * ((jRef.Real * kRef.Real) - (jRef.Imaginary * kRef.Imaginary))) + + (paramsRef.W * ((jRef.Real * kRef.Imaginary) + (jRef.Imaginary * kRef.Real))); + } + } + } + + // Normalize the kernels + float scalar = (float)(1f / Math.Sqrt(total)); + for (int i = 0; i < kernelsSpan.Length; i++) + { + ref Complex64[] kernelsRef = ref Unsafe.Add(ref baseKernelsRef, i); + int length = kernelsRef.Length; + ref Complex64 valueRef = ref kernelsRef[0]; + + for (int j = 0; j < length; j++) + { + Unsafe.Add(ref valueRef, j) *= scalar; + } + } + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + // Preliminary gamma highlight pass + this.ApplyGammaExposure(source.PixelBuffer, sourceRectangle, configuration); + + // Create a 0-filled buffer to use to store the result of the component convolutions + using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean)) + using (Buffer2D + partialValues = configuration.MemoryAllocator.Allocate2D(source.Size()), + firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) + { + // Perform two 1D convolutions for each component in the current instance + ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan()); + for (int i = 0; i < this.kernels.Length; i++) + { + // Compute the resulting complex buffer for the current component + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + Complex64[] kernel = Unsafe.Add(ref baseRef, i); + this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); + this.ApplyConvolution(partialValues, firstPassValues, interest, kernel, configuration); + + // Add the results of the convolution with the current kernel + Vector4 parameters = this.kernelParameters[i]; + this.SumProcessingPartials(processing, partialValues, sourceRectangle, configuration, parameters.Z, parameters.W); + } + + // Apply the inverse gamma exposure pass, and write the final pixel data + this.ApplyInverseGammaExposure(source.PixelBuffer, processing, sourceRectangle, configuration); + } + } + + /// + /// Applies the process to the specified portion of the specified at the specified location + /// and with the specified size. + /// + /// The target values to use to store the results. + /// The source pixels. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// The 1D kernel. + /// The + private void ApplyConvolution( + Buffer2D targetValues, + Buffer2D sourcePixels, + Rectangle sourceRectangle, + Complex64[] kernel, + Configuration configuration) + { + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + int maxY = endY - 1; + int maxX = endX - 1; + + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + int width = workingRectangle.Width; + + ParallelHelper.IterateRows( + workingRectangle, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); + + for (int x = 0; x < width; x++) + { + DenseMatrixUtils.Convolve1D(kernel, sourcePixels, targetRowSpan, y, x, startY, maxY, startX, maxX); + } + } + }); + } + + /// + /// Applies the process to the specified portion of the specified buffer at the specified location + /// and with the specified size. + /// + /// The target values to use to store the results. + /// The source complex values. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// The 1D kernel. + /// The + private void ApplyConvolution( + Buffer2D targetValues, + Buffer2D sourceValues, + Rectangle sourceRectangle, + Complex64[] kernel, + Configuration configuration) + { + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + int maxY = endY - 1; + int maxX = endX - 1; + + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + int width = workingRectangle.Width; + + ParallelHelper.IterateRows( + workingRectangle, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); + + for (int x = 0; x < width; x++) + { + DenseMatrixUtils.Convolve1D(kernel, sourceValues, targetRowSpan, y, x, startY, maxY, startX, maxX); + } + } + }); + } + + /// + /// Applies the gamma correction/highlight to the input pixel buffer. + /// + /// The target pixel buffer to adjust. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// The + private void ApplyGammaExposure( + Buffer2D targetPixels, + Rectangle sourceRectangle, + Configuration configuration) + { + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + int width = workingRectangle.Width; + float gamma = this.gamma; + + ParallelHelper.IterateRowsWithTempBuffer( + workingRectangle, + configuration, + (rows, vectorBuffer) => + { + Span vectorSpan = vectorBuffer.Span; + int length = vectorSpan.Length; + + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan, PixelConversionModifiers.Premultiply); + ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectorSpan); + + for (int x = 0; x < width; x++) + { + ref Vector4 v = ref Unsafe.Add(ref baseRef, x); + v.X = (float)Math.Pow(v.X, gamma); + v.Y = (float)Math.Pow(v.Y, gamma); + v.Z = (float)Math.Pow(v.Z, gamma); + } + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan); + } + }); + } + + /// + /// Applies the inverse gamma correction/highlight pass, and converts the input buffer into pixel values. + /// + /// The target pixels to apply the process to. + /// The source values. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// The + private void ApplyInverseGammaExposure( + Buffer2D targetPixels, + Buffer2D sourceValues, + Rectangle sourceRectangle, + Configuration configuration) + { + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + int width = workingRectangle.Width; + float expGamma = 1 / this.gamma; + + ParallelHelper.IterateRows( + workingRectangle, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = sourceValues.GetRowSpan(y).Slice(startX); + Span targetPixelSpan = targetPixels.GetRowSpan(y).Slice(startX); + ref Vector4 baseRef = ref MemoryMarshal.GetReference(targetRowSpan); + + for (int x = 0; x < width; x++) + { + ref Vector4 v = ref Unsafe.Add(ref baseRef, x); + v.X = (float)Math.Pow(v.X.Clamp(0, float.PositiveInfinity), expGamma); + v.Y = (float)Math.Pow(v.Y.Clamp(0, float.PositiveInfinity), expGamma); + v.Z = (float)Math.Pow(v.Z.Clamp(0, float.PositiveInfinity), expGamma); + } + + PixelOperations.Instance.FromVector4Destructive(configuration, targetRowSpan.Slice(0, width), targetPixelSpan, PixelConversionModifiers.Premultiply); + } + }); + } + + /// + /// Applies the process to the specified portion of the specified at the specified location + /// and with the specified size. + /// + /// The target instance to use to store the results. + /// The source complex pixels. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// The + /// The weight factor for the real component of the complex pixel values. + /// The weight factor for the imaginary component of the complex pixel values. + private void SumProcessingPartials( + Buffer2D targetValues, + Buffer2D sourceValues, + Rectangle sourceRectangle, + Configuration configuration, + float z, + float w) + { + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + int width = workingRectangle.Width; + + ParallelHelper.IterateRows( + workingRectangle, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); + Span sourceRowSpan = sourceValues.GetRowSpan(y).Slice(startX); + ref Vector4 baseTargetRef = ref MemoryMarshal.GetReference(targetRowSpan); + ref ComplexVector4 baseSourceRef = ref MemoryMarshal.GetReference(sourceRowSpan); + + for (int x = 0; x < width; x++) + { + Unsafe.Add(ref baseTargetRef, x) += Unsafe.Add(ref baseSourceRef, x).WeightedSum(z, w); + } + } + }); + } + } +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs index 6d6e22a6a5..4b81aaa64e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + using SixLabors.Memory; namespace SixLabors.ImageSharp.Processing.Processors.Transforms diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs index 404714a54b..3b7dea0955 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath { diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs index f2dab02ef5..017f58ef74 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs @@ -5,6 +5,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; + using SixLabors.Memory; /// /// This benchmark compares different methods for fetching memory data into diff --git a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs index 4b27b7b2da..974099991d 100644 --- a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs +++ b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs @@ -10,6 +10,10 @@ namespace SixLabors.ImageSharp.Tests.Advanced { + using System.Buffers; + + using SixLabors.Memory; + public class AdvancedImageExtensionsTests { public class GetPixelMemory diff --git a/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs index 64a394cc98..894d902b78 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs @@ -1,4 +1,5 @@ -using System.Buffers.Binary; +using System; +using System.Buffers.Binary; using System.Text; using SixLabors.ImageSharp.Formats.Png; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index b1408247cf..81a31e42d3 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -3,7 +3,7 @@ using System.IO; using Xunit; - +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; diff --git a/tests/ImageSharp.Tests/Helpers/GuardTests.cs b/tests/ImageSharp.Tests/Helpers/GuardTests.cs index 6bccea2c3e..b847e581f5 100644 --- a/tests/ImageSharp.Tests/Helpers/GuardTests.cs +++ b/tests/ImageSharp.Tests/Helpers/GuardTests.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Xunit; // ReSharper disable InconsistentNaming diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs index c4be6fbecf..3f4cb8afa2 100644 --- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs @@ -3,7 +3,7 @@ using System; using System.IO; - +using System.Linq; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.PixelFormats; @@ -13,6 +13,9 @@ namespace SixLabors.ImageSharp.Tests { + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + /// /// Tests the class. /// diff --git a/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs b/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs index 975ed84e0b..9d709d488b 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs @@ -4,7 +4,8 @@ using System; using System.IO; using SixLabors.ImageSharp.Formats; - +using SixLabors.ImageSharp.IO; +using Moq; using Xunit; // ReSharper disable InconsistentNaming diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs index 9ca1e30cce..7de1cbb190 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -9,6 +9,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { + using SixLabors.Memory; + public class PorterDuffFunctionsTestsTPixel { private static Span AsSpan(T value) diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs index eea0b5eac5..a044ebae9b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs @@ -2,7 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; - +using System.Collections.Generic; +using System.Text; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.ImageSharp.Tests.TestUtilities; diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs index 429a655834..275afa35d6 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs @@ -5,6 +5,7 @@ using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using Xunit; +using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests.PixelFormats { diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs index 31699fc49d..07b9b1b8c6 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -4,9 +4,10 @@ using System.Collections.Generic; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.ImageSharp.Tests.TestUtilities; - +using SixLabors.Primitives; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Convolution diff --git a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs index d3fc424c9e..ff2b1c702d 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing.Processors; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Effects diff --git a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs index 73558a64a8..d598f0ac88 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs @@ -2,7 +2,8 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Filters diff --git a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs index dde419bce7..446cfa1c33 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs @@ -1,8 +1,10 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System.IO; using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; using Xunit; namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs index 978fd416bc..0301f5c039 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index e57a21290b..3796c51a3b 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -56,7 +56,8 @@ public void VerifyComplexComponents() } // Make sure the kernel components are the same - var processor = new BokehBlurProcessor(10); + var definition = new BokehBlurProcessor(10, BokehBlurProcessor.DefaultComponents, BokehBlurProcessor.DefaultGamma); + var processor = new BokehBlurProcessor(definition); Assert.Equal(components.Count, processor.Kernels.Count); foreach ((Complex64[] a, Complex64[] b) in components.Zip(processor.Kernels, (a, b) => (a, b))) { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs index c2728e0435..f08ec147ec 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs index a6fa5c8f48..6dc9b36309 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs @@ -5,11 +5,8 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -<<<<<<< HEAD using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -======= ->>>>>>> Removed unused using directives using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs index dc169df816..51680eee04 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using SixLabors.ImageSharp.Processing; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs index 1e08836c13..20e12cb7f9 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms diff --git a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs index ccfe8e4c00..9fe2977adb 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs @@ -1,8 +1,10 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index 3e0c68e8b6..b870ddd08a 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -1,12 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -<<<<<<< HEAD using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; -======= -using SixLabors.ImageSharp.PixelFormats; ->>>>>>> Removed unused using directives using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs index e9a6120ca8..38033e80d0 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs index 799794ca4f..1fb745c38f 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; +using SixLabors.Memory; namespace SixLabors.ImageSharp.Tests { diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index 8219920909..6ca86ced6e 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -3,8 +3,10 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; - +using System.Linq; +using System.Reflection; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 78380a015f..23bd0a54c3 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -2,15 +2,12 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; -<<<<<<< HEAD using System.Numerics; using System.Reflection; -======= - ->>>>>>> Removed unused using directives using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs index 0860af1a4e..dae2f0cfe4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + using SixLabors.ImageSharp.PixelFormats; using Xunit.Abstractions; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs index 1e1a45f074..e998ccd3dc 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs @@ -2,7 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; - +using System.Collections.Generic; +using System.Text; using SixLabors.ImageSharp.PixelFormats; using Xunit.Abstractions; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestType.cs b/tests/ImageSharp.Tests/TestUtilities/TestType.cs index 7643fb321e..852aaf2d43 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestType.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestType.cs @@ -1,6 +1,10 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; +using System.Collections.Generic; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests.TestUtilities diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs index 096f78299b..122234ae89 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs @@ -3,6 +3,8 @@ using System; using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats; diff --git a/tests/ImageSharp.Tests/VectorAssert.cs b/tests/ImageSharp.Tests/VectorAssert.cs index 9612882b3d..402d066555 100644 --- a/tests/ImageSharp.Tests/VectorAssert.cs +++ b/tests/ImageSharp.Tests/VectorAssert.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Numerics; - +using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using Xunit; From 32d9077bbbd799efd2f228d3ccbdd9f363cb1cb4 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 Jul 2019 17:04:19 +1000 Subject: [PATCH 58/82] Update Block8x8F.Generated.cs --- .../Jpeg/Components/Block8x8F.Generated.cs | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs index b132ce7a0c..a9e9903a9d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs @@ -1,11 +1,4 @@ - - - - - - - -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -13,7 +6,6 @@ using System.Runtime.CompilerServices; // - namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal partial struct Block8x8F @@ -96,7 +88,6 @@ public void TransposeInto(ref Block8x8F d) d.V5R.W = V7R.Y; d.V6R.W = V7R.Z; d.V7R.W = V7R.W; - } /// @@ -124,7 +115,6 @@ public void NormalizeColorsInplace(float maximum) this.V6R = Vector4.Clamp(this.V6R + COff4, CMin4, CMax4); this.V7L = Vector4.Clamp(this.V7L + COff4, CMin4, CMax4); this.V7R = Vector4.Clamp(this.V7R + COff4, CMin4, CMax4); - } /// @@ -136,39 +126,30 @@ public void NormalizeColorsAndRoundInplaceAvx2(float maximum) Vector off = new Vector(MathF.Ceiling(maximum / 2)); Vector max = new Vector(maximum); - ref Vector row0 = ref Unsafe.As>(ref this.V0L); row0 = NormalizeAndRound(row0, off, max); - ref Vector row1 = ref Unsafe.As>(ref this.V1L); row1 = NormalizeAndRound(row1, off, max); - ref Vector row2 = ref Unsafe.As>(ref this.V2L); row2 = NormalizeAndRound(row2, off, max); - ref Vector row3 = ref Unsafe.As>(ref this.V3L); row3 = NormalizeAndRound(row3, off, max); - ref Vector row4 = ref Unsafe.As>(ref this.V4L); row4 = NormalizeAndRound(row4, off, max); - ref Vector row5 = ref Unsafe.As>(ref this.V5L); row5 = NormalizeAndRound(row5, off, max); - ref Vector row6 = ref Unsafe.As>(ref this.V6L); row6 = NormalizeAndRound(row6, off, max); - ref Vector row7 = ref Unsafe.As>(ref this.V7L); row7 = NormalizeAndRound(row7, off, max); - } /// @@ -249,7 +230,6 @@ public void LoadFromInt16Scalar(ref Block8x8 source) this.V7R.Y = Unsafe.Add(ref selfRef, 61); this.V7R.Z = Unsafe.Add(ref selfRef, 62); this.V7R.W = Unsafe.Add(ref selfRef, 63); - } } } From 512586e2c886ce0b14ea760bc6d8ee73b30bac2b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 Jul 2019 17:07:16 +1000 Subject: [PATCH 59/82] Update GenericBlock8x8.Generated.cs --- .../Jpeg/Components/GenericBlock8x8.Generated.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs index ac0e6a6d26..0cc729371f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs @@ -1,11 +1,4 @@ - - - - - - - -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. // @@ -25,7 +18,6 @@ internal unsafe partial struct GenericBlock8x8 private T _y6_x0, _y6_x1, _y6_x2, _y6_x3, _y6_x4, _y6_x5, _y6_x6, _y6_x7; private T _y7_x0, _y7_x1, _y7_x2, _y7_x3, _y7_x4, _y7_x5, _y7_x6, _y7_x7; - #pragma warning restore 169 } } From 0e6259d9d21dea26c07221600185f5e822d66274 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 29 Jul 2019 12:52:24 +0200 Subject: [PATCH 60/82] Manual checking for files with LF (see gitter) --- .../Input/Jpg/baseline/JpegSnoopReports/Floorplan.jpg.txt | 4 ++-- .../Images/Input/Jpg/baseline/JpegSnoopReports/badrst.jpg.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Floorplan.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Floorplan.jpg.txt index 3afec1c893..a557d16c13 100644 --- a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Floorplan.jpg.txt +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Floorplan.jpg.txt @@ -63,8 +63,8 @@ Start Offset: 0x00000000 Length = 12772 Identifier = [http://ns.adobe.com/xap/1.0/] XMP = - | - |Windows Photo Editor 10.0.10011.163842016-01-02T19:22:28 + | + |Windows Photo Editor 10.0.10011.163842016-01-02T19:22:28 *** Marker: DQT (xFFDB) *** Define a Quantization Table. diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/badrst.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/badrst.jpg.txt index d7c49652e3..3ec02b50d9 100644 --- a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/badrst.jpg.txt +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/badrst.jpg.txt @@ -54,8 +54,8 @@ Start Offset: 0x00000000 Length = 2464 Identifier = [http://ns.adobe.com/xap/1.0/] XMP = - | - |2016-02-28T11:17:08.057 + | + |2016-02-28T11:17:08.057 *** Marker: DQT (xFFDB) *** Define a Quantization Table. From f36564c5e744e7ca92d750e8cd00cd67d145c9da Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 29 Jul 2019 12:59:12 +0200 Subject: [PATCH 61/82] Removed unused using directive --- .../Processing/Processors/Convolution/BoxBlurProcessor.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index 4e56e75d39..726947ac91 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -1,8 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -47,4 +46,4 @@ public IImageProcessor CreatePixelSpecificProcessor() return new BoxBlurProcessor(this); } } -} \ No newline at end of file +} From 9491262b4b4214c273044910107f24b99ee65cd8 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 29 Jul 2019 12:59:26 +0200 Subject: [PATCH 62/82] Added IEquatable interface --- src/ImageSharp/Primitives/ComplexVector4.cs | 23 +++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Primitives/ComplexVector4.cs b/src/ImageSharp/Primitives/ComplexVector4.cs index 4a4e0d6152..b90da65b2d 100644 --- a/src/ImageSharp/Primitives/ComplexVector4.cs +++ b/src/ImageSharp/Primitives/ComplexVector4.cs @@ -1,6 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using System.Runtime.CompilerServices; @@ -9,7 +10,7 @@ namespace SixLabors.ImageSharp.Primitives /// /// A vector with 4 values of type . /// - internal struct ComplexVector4 + internal struct ComplexVector4 : IEquatable { /// /// The real part of the complex vector @@ -40,5 +41,23 @@ public void Sum(in ComplexVector4 value) /// The resulting value [MethodImpl(InliningOptions.ShortMethod)] public Vector4 WeightedSum(float a, float b) => (this.Real * a) + (this.Imaginary * b); + + /// + public bool Equals(ComplexVector4 other) + { + return this.Real.Equals(other.Real) && this.Imaginary.Equals(other.Imaginary); + } + + /// + public override bool Equals(object obj) => obj is ComplexVector4 other && this.Equals(other); + + /// + public override int GetHashCode() + { + unchecked + { + return (this.Real.GetHashCode() * 397) ^ this.Imaginary.GetHashCode(); + } + } } } From 9edf46b511c363839c3241ff38bb401c505ae8fc Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 29 Jul 2019 13:01:35 +0200 Subject: [PATCH 63/82] Added IEquatable interface --- .../Primitives/BokehBlurParameters.cs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Primitives/BokehBlurParameters.cs b/src/ImageSharp/Primitives/BokehBlurParameters.cs index d653115e8e..3e2cc143a0 100644 --- a/src/ImageSharp/Primitives/BokehBlurParameters.cs +++ b/src/ImageSharp/Primitives/BokehBlurParameters.cs @@ -1,12 +1,14 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + namespace SixLabors.ImageSharp.Primitives { /// /// A that contains parameters to apply a bokeh blur filter /// - internal readonly struct BokehBlurParameters + internal readonly struct BokehBlurParameters : IEquatable { /// /// The size of the convolution kernel to use when applying the bokeh blur @@ -28,5 +30,23 @@ public BokehBlurParameters(int radius, int components) this.Radius = radius; this.Components = components; } + + /// + public bool Equals(BokehBlurParameters other) + { + return this.Radius.Equals(other.Radius) && this.Components.Equals(other.Components); + } + + /// + public override bool Equals(object obj) => obj is BokehBlurParameters other && this.Equals(other); + + /// + public override int GetHashCode() + { + unchecked + { + return (this.Radius.GetHashCode() * 397) ^ this.Components.GetHashCode(); + } + } } } From db581635595e6db4dff75fbe726bfc67f51b8188 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 29 Jul 2019 13:05:14 +0200 Subject: [PATCH 64/82] Moved bokeh blur parameters types --- .../Processors/Convolution/BokehBlurProcessor{TPixel}.cs | 1 + .../Processors/Convolution/Parameters}/BokehBlurKernelData.cs | 4 +++- .../Processors/Convolution/Parameters}/BokehBlurParameters.cs | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) rename src/ImageSharp/{Primitives => Processing/Processors/Convolution/Parameters}/BokehBlurKernelData.cs (92%) rename src/ImageSharp/{Primitives => Processing/Processors/Convolution/Parameters}/BokehBlurParameters.cs (95%) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index b6204e327e..c3cb548bbf 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -11,6 +11,7 @@ using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; +using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters; using SixLabors.Memory; using SixLabors.Primitives; diff --git a/src/ImageSharp/Primitives/BokehBlurKernelData.cs b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelData.cs similarity index 92% rename from src/ImageSharp/Primitives/BokehBlurKernelData.cs rename to src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelData.cs index 4fe83a489c..4338bcf6b9 100644 --- a/src/ImageSharp/Primitives/BokehBlurKernelData.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelData.cs @@ -3,7 +3,9 @@ using System.Numerics; -namespace SixLabors.ImageSharp.Primitives +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters { /// /// A that contains data about a set of bokeh blur kernels diff --git a/src/ImageSharp/Primitives/BokehBlurParameters.cs b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurParameters.cs similarity index 95% rename from src/ImageSharp/Primitives/BokehBlurParameters.cs rename to src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurParameters.cs index 3e2cc143a0..73688c5869 100644 --- a/src/ImageSharp/Primitives/BokehBlurParameters.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurParameters.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Primitives +namespace SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters { /// /// A that contains parameters to apply a bokeh blur filter From 24cc75ab7037254f7ec5f15d5de212e5b67fb8a0 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 29 Jul 2019 13:08:07 +0200 Subject: [PATCH 65/82] Added reference to original source code --- .../Processors/Convolution/BokehBlurProcessor{TPixel}.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index c3cb548bbf..50b06dbdbe 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -21,6 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Applies bokeh blur processing to the image. /// /// The pixel format. + /// This processor is based on the code from Mike Pound, see github.com/mikepound/convolve. internal class BokehBlurProcessor : ImageProcessor where TPixel : struct, IPixel { From 0a77f69ebf043b7d2811360718c280a94b7746f8 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 29 Jul 2019 13:14:18 +0200 Subject: [PATCH 66/82] Complex convolution methods moved to another class --- .../Common/Helpers/Buffer2DUtils.cs | 105 ++++++++++++++++++ .../Common/Helpers/DenseMatrixUtils.cs | 86 -------------- .../Convolution/BokehBlurProcessor{TPixel}.cs | 4 +- 3 files changed, 107 insertions(+), 88 deletions(-) create mode 100644 src/ImageSharp/Common/Helpers/Buffer2DUtils.cs diff --git a/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs b/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs new file mode 100644 index 0000000000..0c22aa68ff --- /dev/null +++ b/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs @@ -0,0 +1,105 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for . + /// TODO: One day rewrite all this to use SIMD intrinsics. There's a lot of scope for improvement. + /// + internal static class Buffer2DUtils + { + /// + /// Computes the sum of vectors in weighted by the kernel weight values. + /// + /// The pixel format. + /// The 1D convolution kernel. + /// The source frame. + /// The target row. + /// The current row. + /// The current column. + /// The minimum working area row. + /// The maximum working area row. + /// The minimum working area column. + /// The maximum working area column. + public static void Convolve4( + Span kernel, + Buffer2D sourcePixels, + Span targetRow, + int row, + int column, + int minRow, + int maxRow, + int minColumn, + int maxColumn) + where TPixel : struct, IPixel + { + ComplexVector4 vector = default; + int kernelLength = kernel.Length; + int radiusY = kernelLength >> 1; + int sourceOffsetColumnBase = column + minColumn; + ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel); + + for (int i = 0; i < kernelLength; i++) + { + int offsetY = (row + i - radiusY).Clamp(minRow, maxRow); + int offsetX = sourceOffsetColumnBase.Clamp(minColumn, maxColumn); + Span sourceRowSpan = sourcePixels.GetRowSpan(offsetY); + var currentColor = sourceRowSpan[offsetX].ToVector4(); + + vector.Sum(Unsafe.Add(ref baseRef, i) * currentColor); + } + + targetRow[column] = vector; + } + + /// + /// Computes the sum of vectors in weighted by the kernel weight values. + /// + /// The 1D convolution kernel. + /// The source frame. + /// The target row. + /// The current row. + /// The current column. + /// The minimum working area row. + /// The maximum working area row. + /// The minimum working area column. + /// The maximum working area column. + public static void Convolve4( + Span kernel, + Buffer2D sourceValues, + Span targetRow, + int row, + int column, + int minRow, + int maxRow, + int minColumn, + int maxColumn) + { + ComplexVector4 vector = default; + int kernelLength = kernel.Length; + int radiusX = kernelLength >> 1; + int sourceOffsetColumnBase = column + minColumn; + + int offsetY = row.Clamp(minRow, maxRow); + ref ComplexVector4 sourceRef = ref MemoryMarshal.GetReference(sourceValues.GetRowSpan(offsetY)); + ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel); + + for (int x = 0; x < kernelLength; x++) + { + int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(minColumn, maxColumn); + vector.Sum(Unsafe.Add(ref baseRef, x) * Unsafe.Add(ref sourceRef, offsetX)); + } + + targetRow[column] = vector; + } + } +} diff --git a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs index 75795fca46..b6bbd4023f 100644 --- a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs +++ b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs @@ -4,7 +4,6 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; @@ -248,91 +247,6 @@ public static void Convolve4( target = vector; } - /// - /// Computes the sum of vectors in weighted by the kernel weight values. - /// - /// The pixel format. - /// The 1D convolution kernel. - /// The source frame. - /// The target row. - /// The current row. - /// The current column. - /// The minimum working area row. - /// The maximum working area row. - /// The minimum working area column. - /// The maximum working area column. - public static void Convolve1D( - Span kernel, - Buffer2D sourcePixels, - Span targetRow, - int row, - int column, - int minRow, - int maxRow, - int minColumn, - int maxColumn) - where TPixel : struct, IPixel - { - ComplexVector4 vector = default; - int kernelLength = kernel.Length; - int radiusY = kernelLength >> 1; - int sourceOffsetColumnBase = column + minColumn; - ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel); - - for (int i = 0; i < kernelLength; i++) - { - int offsetY = (row + i - radiusY).Clamp(minRow, maxRow); - int offsetX = sourceOffsetColumnBase.Clamp(minColumn, maxColumn); - Span sourceRowSpan = sourcePixels.GetRowSpan(offsetY); - var currentColor = sourceRowSpan[offsetX].ToVector4(); - - vector.Sum(Unsafe.Add(ref baseRef, i) * currentColor); - } - - targetRow[column] = vector; - } - - /// - /// Computes the sum of vectors in weighted by the kernel weight values. - /// - /// The 1D convolution kernel. - /// The source frame. - /// The target row. - /// The current row. - /// The current column. - /// The minimum working area row. - /// The maximum working area row. - /// The minimum working area column. - /// The maximum working area column. - public static void Convolve1D( - Span kernel, - Buffer2D sourceValues, - Span targetRow, - int row, - int column, - int minRow, - int maxRow, - int minColumn, - int maxColumn) - { - ComplexVector4 vector = default; - int kernelLength = kernel.Length; - int radiusX = kernelLength >> 1; - int sourceOffsetColumnBase = column + minColumn; - - int offsetY = row.Clamp(minRow, maxRow); - ref ComplexVector4 sourceRef = ref MemoryMarshal.GetReference(sourceValues.GetRowSpan(offsetY)); - ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel); - - for (int x = 0; x < kernelLength; x++) - { - int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(minColumn, maxColumn); - vector.Sum(Unsafe.Add(ref baseRef, x) * Unsafe.Add(ref sourceRef, offsetX)); - } - - targetRow[column] = vector; - } - [MethodImpl(InliningOptions.ShortMethod)] private static void ConvolveImpl( in DenseMatrix matrix, diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 50b06dbdbe..ec553ac990 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -334,7 +334,7 @@ private void ApplyConvolution( for (int x = 0; x < width; x++) { - DenseMatrixUtils.Convolve1D(kernel, sourcePixels, targetRowSpan, y, x, startY, maxY, startX, maxX); + Buffer2DUtils.Convolve4(kernel, sourcePixels, targetRowSpan, y, x, startY, maxY, startX, maxX); } } }); @@ -379,7 +379,7 @@ private void ApplyConvolution( for (int x = 0; x < width; x++) { - DenseMatrixUtils.Convolve1D(kernel, sourceValues, targetRowSpan, y, x, startY, maxY, startX, maxX); + Buffer2DUtils.Convolve4(kernel, sourceValues, targetRowSpan, y, x, startY, maxY, startX, maxX); } } }); From 04984cd65006b8794fcef38564ef1414654eecb5 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 29 Jul 2019 13:17:26 +0200 Subject: [PATCH 67/82] Switched to MathF in the bokeh blur processor --- .../Convolution/BokehBlurProcessor{TPixel}.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index ec553ac990..22fd4dfb40 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -210,8 +210,8 @@ private Complex64[] CreateComplex1DKernel(float a, float b) // Fill in the complex kernel values Unsafe.Add(ref baseRef, i) = new Complex64( - (float)(Math.Exp(-a * value) * Math.Cos(b * value)), - (float)(Math.Exp(-a * value) * Math.Sin(b * value))); + MathF.Exp(-a * value) * MathF.Cos(b * value), + MathF.Exp(-a * value) * MathF.Sin(b * value)); } return kernel; @@ -223,7 +223,7 @@ private Complex64[] CreateComplex1DKernel(float a, float b) private void NormalizeKernels() { // Calculate the complex weighted sum - double total = 0; + float total = 0; Span kernelsSpan = this.kernels.AsSpan(); ref Complex64[] baseKernelsRef = ref MemoryMarshal.GetReference(kernelsSpan); ref Vector4 baseParamsRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan()); @@ -249,7 +249,7 @@ private void NormalizeKernels() } // Normalize the kernels - float scalar = (float)(1f / Math.Sqrt(total)); + float scalar = 1f / MathF.Sqrt(total); for (int i = 0; i < kernelsSpan.Length; i++) { ref Complex64[] kernelsRef = ref Unsafe.Add(ref baseKernelsRef, i); @@ -424,9 +424,9 @@ private void ApplyGammaExposure( for (int x = 0; x < width; x++) { ref Vector4 v = ref Unsafe.Add(ref baseRef, x); - v.X = (float)Math.Pow(v.X, gamma); - v.Y = (float)Math.Pow(v.Y, gamma); - v.Z = (float)Math.Pow(v.Z, gamma); + v.X = MathF.Pow(v.X, gamma); + v.Y = MathF.Pow(v.Y, gamma); + v.Z = MathF.Pow(v.Z, gamma); } PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan); @@ -472,9 +472,9 @@ private void ApplyInverseGammaExposure( for (int x = 0; x < width; x++) { ref Vector4 v = ref Unsafe.Add(ref baseRef, x); - v.X = (float)Math.Pow(v.X.Clamp(0, float.PositiveInfinity), expGamma); - v.Y = (float)Math.Pow(v.Y.Clamp(0, float.PositiveInfinity), expGamma); - v.Z = (float)Math.Pow(v.Z.Clamp(0, float.PositiveInfinity), expGamma); + v.X = MathF.Pow(v.X.Clamp(0, float.PositiveInfinity), expGamma); + v.Y = MathF.Pow(v.Y.Clamp(0, float.PositiveInfinity), expGamma); + v.Z = MathF.Pow(v.Z.Clamp(0, float.PositiveInfinity), expGamma); } PixelOperations.Instance.FromVector4Destructive(configuration, targetRowSpan.Slice(0, width), targetPixelSpan, PixelConversionModifiers.Premultiply); From a70518bcdad126be73aa97b5a1398b4af4c52bce Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 29 Jul 2019 13:50:37 +0200 Subject: [PATCH 68/82] Switched to Vector4.Clamp --- .../Convolution/BokehBlurProcessor{TPixel}.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 22fd4dfb40..d33d89c8da 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -463,6 +463,9 @@ private void ApplyInverseGammaExposure( configuration, rows => { + Vector4 low = Vector4.Zero; + var high = new Vector4(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); + for (int y = rows.Min; y < rows.Max; y++) { Span targetRowSpan = sourceValues.GetRowSpan(y).Slice(startX); @@ -472,9 +475,10 @@ private void ApplyInverseGammaExposure( for (int x = 0; x < width; x++) { ref Vector4 v = ref Unsafe.Add(ref baseRef, x); - v.X = MathF.Pow(v.X.Clamp(0, float.PositiveInfinity), expGamma); - v.Y = MathF.Pow(v.Y.Clamp(0, float.PositiveInfinity), expGamma); - v.Z = MathF.Pow(v.Z.Clamp(0, float.PositiveInfinity), expGamma); + var clamp = Vector4.Clamp(v, low, high); + v.X = MathF.Pow(clamp.X, expGamma); + v.Y = MathF.Pow(clamp.Y, expGamma); + v.Z = MathF.Pow(clamp.Z, expGamma); } PixelOperations.Instance.FromVector4Destructive(configuration, targetRowSpan.Slice(0, width), targetPixelSpan, PixelConversionModifiers.Premultiply); From 8aec5e04df82a92563cd094c92611c49377b4eeb Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 1 Aug 2019 23:30:51 +0200 Subject: [PATCH 69/82] Added Buffer2D.Slice API --- src/ImageSharp/Memory/Buffer2D{T}.cs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 41a560cdb6..82a98bfc63 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -61,13 +61,31 @@ public Buffer2D(MemorySource memorySource, int width, int height) [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - ImageSharp.DebugGuard.MustBeLessThan(x, this.Width, nameof(x)); - ImageSharp.DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); + DebugGuard.MustBeLessThan(x, this.Width, nameof(x)); + DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); + Span span = this.Span; return ref span[(this.Width * y) + x]; } } + /// + /// Creates a new instance that maps to a target rows interval from the current instance. + /// + /// The target vertical offset for the rows interval to retrieve. + /// The desired number of rows to extract. + /// The new instance with the requested rows interval. + public Buffer2D Slice(int y, int h) + { + DebugGuard.MustBeGreaterThanOrEqualTo(y, 0, nameof(y)); + DebugGuard.MustBeGreaterThan(h, 0, nameof(h)); + DebugGuard.MustBeLessThanOrEqualTo(y + h, this.Height, nameof(h)); + + Memory slice = this.Memory.Slice(y * this.Width, h * this.Width); + var memory = new MemorySource(slice); + return new Buffer2D(memory, this.Width, h); + } + /// /// Disposes the instance /// @@ -98,4 +116,4 @@ private static void SwapDimensionData(Buffer2D a, Buffer2D b) a.Height = bSize.Height; } } -} \ No newline at end of file +} From 3f24ceedf7830133306a5c2e2f9fd3fab35263e1 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 1 Aug 2019 23:36:42 +0200 Subject: [PATCH 70/82] Added BokehBlurExecutionMode enum --- .../Parameters/BokehBlurExecutionMode.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurExecutionMode.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurExecutionMode.cs b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurExecutionMode.cs new file mode 100644 index 0000000000..519e311484 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurExecutionMode.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters +{ + /// + /// An that indicates execution options for the . + /// + public enum BokehBlurExecutionMode + { + /// + /// Indicates that the maximum performance should be prioritized over memory usage. + /// + PreferMaximumPerformance, + + /// + /// Indicates that the memory usage should be prioritized over raw performance. + /// + PreferLowMemoryUsage + } +} From 06dfb02346e561eec191d82066989b30f51e88f2 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 1 Aug 2019 23:43:59 +0200 Subject: [PATCH 71/82] Added new bokeh blur processor constructors --- .../Convolution/BokehBlurProcessor.cs | 45 ++++++++++++++++++- .../Convolution/BokehBlurProcessor{TPixel}.cs | 8 +++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index b6a65821c6..bda8767447 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -25,11 +26,27 @@ public sealed class BokehBlurProcessor : IImageProcessor /// public const float DefaultGamma = 3F; + /// + /// The default execution mode used by the parameterless constructor. + /// + public const BokehBlurExecutionMode DefaultExecutionMode = BokehBlurExecutionMode.PreferLowMemoryUsage; + /// /// Initializes a new instance of the class. /// public BokehBlurProcessor() - : this(DefaultRadius, DefaultComponents, DefaultGamma) + : this(DefaultRadius, DefaultComponents, DefaultGamma, DefaultExecutionMode) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The execution mode to use when applying the processor. + /// + public BokehBlurProcessor(BokehBlurExecutionMode executionMode) + : this(DefaultRadius, DefaultComponents, DefaultGamma, executionMode) { } @@ -46,12 +63,33 @@ public BokehBlurProcessor() /// The gamma highlight factor to use to further process the image. /// public BokehBlurProcessor(int radius, int components, float gamma) + : this(radius, components, gamma, DefaultExecutionMode) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The 'radius' value representing the size of the area to sample. + /// + /// + /// The number of components to use to approximate the original 2D bokeh blur convolution kernel. + /// + /// + /// The gamma highlight factor to use to further process the image. + /// + /// + /// The execution mode to use when applying the processor. + /// + public BokehBlurProcessor(int radius, int components, float gamma, BokehBlurExecutionMode executionMode) { Guard.MustBeGreaterThanOrEqualTo(gamma, 1, nameof(gamma)); this.Radius = radius; this.Components = components; this.Gamma = gamma; + this.ExecutionMode = executionMode; } /// @@ -69,6 +107,11 @@ public BokehBlurProcessor(int radius, int components, float gamma) /// public float Gamma { get; } + /// + /// Gets the exection mode to use when applying the effect. + /// + public BokehBlurExecutionMode ExecutionMode { get; } + /// public IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index d33d89c8da..d9402b2d7a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -36,7 +36,12 @@ internal class BokehBlurProcessor : ImageProcessor private readonly float gamma; /// - /// The maximum size of the kernel in either direction. + /// The execution mode to use when applying the effect + /// + private readonly BokehBlurExecutionMode executionMode; + + /// + /// The maximum size of the kernel in either direction /// private readonly int kernelSize; @@ -75,6 +80,7 @@ public BokehBlurProcessor(BokehBlurProcessor definition) this.kernelSize = (this.radius * 2) + 1; this.componentsCount = definition.Components; this.gamma = definition.Gamma; + this.executionMode = definition.ExecutionMode; // Reuse the initialized values from the cache, if possible var parameters = new BokehBlurParameters(this.radius, this.componentsCount); From f1a0c378a987a99036fd03d997d5200de571dcd1 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 1 Aug 2019 23:44:12 +0200 Subject: [PATCH 72/82] Added new bokeh blur extension overloads with execution mode --- .../Extensions/BokehBlurExtensions.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs index 2bbdd03b05..6524018115 100644 --- a/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Convolution; +using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing @@ -19,6 +20,15 @@ public static class BokehBlurExtensions public static IImageProcessingContext BokehBlur(this IImageProcessingContext source) => source.ApplyProcessor(new BokehBlurProcessor()); + /// + /// Applies a bokeh blur to the image. + /// + /// The image this method extends. + /// The execution mode to use when applying the processor. + /// The to allow chaining of operations. + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, BokehBlurExecutionMode executionMode) + => source.ApplyProcessor(new BokehBlurProcessor(executionMode)); + /// /// Applies a bokeh blur to the image. /// @@ -30,6 +40,18 @@ public static IImageProcessingContext BokehBlur(this IImageProcessingContext sou public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma) => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma)); + /// + /// Applies a bokeh blur to the image. + /// + /// The image this method extends. + /// The 'radius' value representing the size of the area to sample. + /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. + /// The gamma highlight factor to use to emphasize bright spots in the source image + /// The execution mode to use when applying the processor. + /// The to allow chaining of operations. + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, BokehBlurExecutionMode executionMode) + => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma, executionMode)); + /// /// Applies a bokeh blur to the image. /// @@ -41,6 +63,18 @@ public static IImageProcessingContext BokehBlur(this IImageProcessingContext sou public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new BokehBlurProcessor(), rectangle); + /// + /// Applies a bokeh blur to the image. + /// + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The execution mode to use when applying the processor. + /// The to allow chaining of operations. + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle, BokehBlurExecutionMode executionMode) + => source.ApplyProcessor(new BokehBlurProcessor(executionMode), rectangle); + /// /// Applies a bokeh blur to the image. /// @@ -54,5 +88,20 @@ public static IImageProcessingContext BokehBlur(this IImageProcessingContext sou /// The to allow chaining of operations. public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle) => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), rectangle); + + /// + /// Applies a bokeh blur to the image. + /// + /// The image this method extends. + /// The 'radius' value representing the size of the area to sample. + /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. + /// The gamma highlight factor to use to emphasize bright spots in the source image + /// The execution mode to use when applying the processor. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The to allow chaining of operations. + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, BokehBlurExecutionMode executionMode, Rectangle rectangle) + => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma, executionMode), rectangle); } } From ebd64f60dae890bae957b2bad78e0e582e552210 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 1 Aug 2019 23:59:33 +0200 Subject: [PATCH 73/82] Code refactoring in preparation for the execution mode switch --- .../Convolution/BokehBlurProcessor{TPixel}.cs | 53 +++++++++++++------ 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index d9402b2d7a..dead3acc1d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -277,23 +277,13 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source // Create a 0-filled buffer to use to store the result of the component convolutions using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean)) - using (Buffer2D - partialValues = configuration.MemoryAllocator.Allocate2D(source.Size()), - firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) { - // Perform two 1D convolutions for each component in the current instance - ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan()); - for (int i = 0; i < this.kernels.Length; i++) + // Create the temporary complex buffers to store the results of each convolution component to aggregate + using (Buffer2D + firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size()), + secondPassBuffer = configuration.MemoryAllocator.Allocate2D(source.Size())) { - // Compute the resulting complex buffer for the current component - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - Complex64[] kernel = Unsafe.Add(ref baseRef, i); - this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration); - this.ApplyConvolution(partialValues, firstPassValues, interest, kernel, configuration); - - // Add the results of the convolution with the current kernel - Vector4 parameters = this.kernelParameters[i]; - this.SumProcessingPartials(processing, partialValues, sourceRectangle, configuration, parameters.Z, parameters.W); + this.OnFrameApplyCore(source, sourceRectangle, configuration, processing, firstPassValues, secondPassBuffer); } // Apply the inverse gamma exposure pass, and write the final pixel data @@ -301,6 +291,39 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source } } + /// + /// Computes and aggregates the convolution for each complex kernel component in the processor. + /// + /// The source image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + /// The buffer with the raw pixel data to use to aggregate the results of each convolution. + /// The complex buffer to use for the first 1D convolution pass for each kernel. + /// The complex buffer to use for the second 1D convolution pass for each kernel. + private void OnFrameApplyCore( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration, + Buffer2D processingBuffer, + Buffer2D firstPassBuffer, + Buffer2D secondPassBuffer) + { + // Perform two 1D convolutions for each component in the current instance + ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan()); + for (int i = 0; i < this.kernels.Length; i++) + { + // Compute the resulting complex buffer for the current component + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + Complex64[] kernel = Unsafe.Add(ref baseRef, i); + this.ApplyConvolution(firstPassBuffer, source.PixelBuffer, interest, kernel, configuration); + this.ApplyConvolution(secondPassBuffer, firstPassBuffer, interest, kernel, configuration); + + // Add the results of the convolution with the current kernel + Vector4 parameters = this.kernelParameters[i]; + this.SumProcessingPartials(processingBuffer, secondPassBuffer, sourceRectangle, configuration, parameters.Z, parameters.W); + } + } + /// /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. From 5fc559a7437a0315e5f6a37c8efac74cf37e28d8 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 2 Aug 2019 00:05:57 +0200 Subject: [PATCH 74/82] Implemented execution mode switch in the bokeh processor --- .../Convolution/BokehBlurProcessor{TPixel}.cs | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index dead3acc1d..48ed872180 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -278,12 +278,26 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source // Create a 0-filled buffer to use to store the result of the component convolutions using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean)) { - // Create the temporary complex buffers to store the results of each convolution component to aggregate - using (Buffer2D - firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size()), - secondPassBuffer = configuration.MemoryAllocator.Allocate2D(source.Size())) + if (this.executionMode == BokehBlurExecutionMode.PreferLowMemoryUsage) { - this.OnFrameApplyCore(source, sourceRectangle, configuration, processing, firstPassValues, secondPassBuffer); + // Memory usage priority: allocate a shared buffer and execute the second convolution in sequential mode + using (Buffer2D buffer = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height + this.radius)) + { + Buffer2D firstPassBuffer = buffer.Slice(this.radius, source.Height); + Buffer2D secondPassBuffer = buffer.Slice(0, source.Height); + + this.OnFrameApplyCore(source, sourceRectangle, configuration, processing, firstPassBuffer, secondPassBuffer); + } + } + else + { + // Performance priority: allocate two independent buffers and execute both convolutions in parallel mode + using (Buffer2D + firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size()), + secondPassBuffer = configuration.MemoryAllocator.Allocate2D(source.Size())) + { + this.OnFrameApplyCore(source, sourceRectangle, configuration, processing, firstPassValues, secondPassBuffer); + } } // Apply the inverse gamma exposure pass, and write the final pixel data @@ -397,6 +411,12 @@ private void ApplyConvolution( var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); int width = workingRectangle.Width; + if (this.executionMode == BokehBlurExecutionMode.PreferLowMemoryUsage) + { + configuration = configuration.Clone(); + configuration.MaxDegreeOfParallelism = 1; + } + ParallelHelper.IterateRows( workingRectangle, configuration, From 9aa75ccf4eae64a8be5be0f3a1cdb6d625a46da8 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 5 Aug 2019 14:06:04 +0200 Subject: [PATCH 75/82] Moved BokehBlurExecutionMode struct --- .../Convolution/Parameters => }/BokehBlurExecutionMode.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename src/ImageSharp/Processing/{Processors/Convolution/Parameters => }/BokehBlurExecutionMode.cs (85%) diff --git a/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurExecutionMode.cs b/src/ImageSharp/Processing/BokehBlurExecutionMode.cs similarity index 85% rename from src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurExecutionMode.cs rename to src/ImageSharp/Processing/BokehBlurExecutionMode.cs index 519e311484..bc44dca03c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurExecutionMode.cs +++ b/src/ImageSharp/Processing/BokehBlurExecutionMode.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters +using SixLabors.ImageSharp.Processing.Processors.Convolution; + +namespace SixLabors.ImageSharp.Processing { /// /// An that indicates execution options for the . From 0869492389502cba2c1d48196f122cdcc5548ebf Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 5 Aug 2019 14:14:27 +0200 Subject: [PATCH 76/82] Removed unused using directives --- src/ImageSharp/Color/Color.cs | 2 -- src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs | 1 - src/ImageSharp/Common/Helpers/SimdUtils.cs | 3 --- .../Formats/Jpeg/Components/Decoder/IRawJpegData.cs | 1 - .../Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs | 1 - src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs | 1 - src/ImageSharp/Memory/Buffer2DExtensions.cs | 1 - .../MetaData/Profiles/ICC/DataReader/IccDataReader.cs | 1 - src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs | 1 - .../PixelImplementations/Rgba32.PixelOperations.cs | 1 - src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs | 2 -- .../PixelFormats/Utils/Vector4Converters.Default.cs | 2 -- .../PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs | 2 -- src/ImageSharp/Processing/Extensions/AutoOrientExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/BlackWhiteExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/BoxBlurExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/BrightnessExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/FilterExtensions.cs | 1 - .../Processing/Extensions/GaussianBlurExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/HueExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/InvertExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/KodachromeExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/LomographExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/OpacityExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/PolaroidExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/ResizeExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/RotateFlipExtensions.cs | 2 -- src/ImageSharp/Processing/Extensions/SaturateExtensions.cs | 1 - src/ImageSharp/Processing/Extensions/SepiaExtensions.cs | 1 - .../Processors/Binarization/BinaryErrorDiffusionProcessor.cs | 2 -- .../Processors/Binarization/BinaryThresholdProcessor.cs | 2 -- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 1 - .../Processors/Convolution/GaussianBlurProcessor.cs | 1 - .../Processors/Convolution/GaussianSharpenProcessor.cs | 1 - .../Processors/Convolution/Laplacian3x3Processor.cs | 2 -- .../Processors/Convolution/RobertsCrossProcessor.cs | 2 -- .../Processing/Processors/Convolution/RobinsonProcessor.cs | 3 --- .../Processing/Processors/Convolution/ScharrProcessor.cs | 2 -- .../Processing/Processors/Convolution/SobelProcessor.cs | 3 --- .../Processors/Dithering/ErrorDiffusionPaletteProcessor.cs | 2 -- .../Processing/Processors/Filters/GrayscaleBt709Processor.cs | 3 --- .../Processing/Processors/Filters/KodachromeProcessor.cs | 2 -- .../Processing/Processors/Filters/OpacityProcessor.cs | 2 -- .../Processors/Quantization/WebSafePaletteQuantizer.cs | 1 - .../Processors/Quantization/WernerPaletteQuantizer.cs | 1 - .../Processing/Processors/Transforms/AutoOrientProcessor.cs | 1 - .../Processing/Processors/Transforms/Resize/ResizeKernel.cs | 1 - .../Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs | 2 -- .../Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs | 4 ---- .../Processing/Processors/Transforms/SkewProcessor.cs | 1 - .../Processing/Processors/Transforms/TransformProcessor.cs | 1 - src/ImageSharp/Properties/AssemblyInfo.cs | 2 -- 54 files changed, 80 deletions(-) diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 2547e6d873..4bdbe088ca 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers.Binary; -using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs index 85c9f00748..b16bbc86b6 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs index 867e7b9de1..45761a0d07 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -7,9 +7,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Tuples; - namespace SixLabors.ImageSharp { /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs index ace8d7215b..2f393fadae 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; using SixLabors.Primitives; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs index 301079b6ae..883a085b5a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs @@ -4,7 +4,6 @@ using System; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder diff --git a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs index c795ccc8b5..3d1e22a99d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs @@ -7,7 +7,6 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Formats.Jpeg.Components diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 61fcb99db2..096493f2ba 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs index 7d694bec6e..bbfe909e01 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Text; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs index 195a8bedb0..dd12074352 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Numerics; using System.Runtime.CompilerServices; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs index 3da5d1855b..da02f374e1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs @@ -6,7 +6,6 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats.Utils; -using SixLabors.Memory; namespace SixLabors.ImageSharp.PixelFormats { diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 2b2d79c732..f557f348a1 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -4,8 +4,6 @@ using System; using System.Buffers; using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs index 4c4e60276d..e67bd9d53e 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs @@ -6,8 +6,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - namespace SixLabors.ImageSharp.PixelFormats.Utils { /// diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs index fe8d7dec3b..0350c669ab 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -7,8 +7,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - namespace SixLabors.ImageSharp.PixelFormats.Utils { /// diff --git a/src/ImageSharp/Processing/Extensions/AutoOrientExtensions.cs b/src/ImageSharp/Processing/Extensions/AutoOrientExtensions.cs index a831e2d9af..984081dffe 100644 --- a/src/ImageSharp/Processing/Extensions/AutoOrientExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/AutoOrientExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing diff --git a/src/ImageSharp/Processing/Extensions/BlackWhiteExtensions.cs b/src/ImageSharp/Processing/Extensions/BlackWhiteExtensions.cs index ee34cd99e2..c148ccbcb9 100644 --- a/src/ImageSharp/Processing/Extensions/BlackWhiteExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/BlackWhiteExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs index 6524018115..ef20f940af 100644 --- a/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Convolution; -using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing diff --git a/src/ImageSharp/Processing/Extensions/BoxBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/BoxBlurExtensions.cs index f3400c24e6..42dfd425cc 100644 --- a/src/ImageSharp/Processing/Extensions/BoxBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/BoxBlurExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/BrightnessExtensions.cs b/src/ImageSharp/Processing/Extensions/BrightnessExtensions.cs index db84091763..8e43f06c5a 100644 --- a/src/ImageSharp/Processing/Extensions/BrightnessExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/BrightnessExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/FilterExtensions.cs b/src/ImageSharp/Processing/Extensions/FilterExtensions.cs index 5a66502ce5..662e3a6e16 100644 --- a/src/ImageSharp/Processing/Extensions/FilterExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/FilterExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/GaussianBlurExtensions.cs index e527a14b73..858e3213b1 100644 --- a/src/ImageSharp/Processing/Extensions/GaussianBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/GaussianBlurExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs b/src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs index a87341025d..a4bfaa516c 100644 --- a/src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/HueExtensions.cs b/src/ImageSharp/Processing/Extensions/HueExtensions.cs index 3c1239da67..3955ea7f6e 100644 --- a/src/ImageSharp/Processing/Extensions/HueExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/HueExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/InvertExtensions.cs b/src/ImageSharp/Processing/Extensions/InvertExtensions.cs index c45f24c2ea..16c7a89178 100644 --- a/src/ImageSharp/Processing/Extensions/InvertExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/InvertExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/KodachromeExtensions.cs b/src/ImageSharp/Processing/Extensions/KodachromeExtensions.cs index 810094a180..6c9b279835 100644 --- a/src/ImageSharp/Processing/Extensions/KodachromeExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/KodachromeExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/LomographExtensions.cs b/src/ImageSharp/Processing/Extensions/LomographExtensions.cs index dd7ab21ec1..c2b6ac0804 100644 --- a/src/ImageSharp/Processing/Extensions/LomographExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/LomographExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/OpacityExtensions.cs b/src/ImageSharp/Processing/Extensions/OpacityExtensions.cs index ecf6ce783e..9c67113ecf 100644 --- a/src/ImageSharp/Processing/Extensions/OpacityExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/OpacityExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/PolaroidExtensions.cs b/src/ImageSharp/Processing/Extensions/PolaroidExtensions.cs index eace463579..6b6d43d5b8 100644 --- a/src/ImageSharp/Processing/Extensions/PolaroidExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/PolaroidExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs b/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs index 4578b4353f..81b1c2c663 100644 --- a/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/RotateFlipExtensions.cs b/src/ImageSharp/Processing/Extensions/RotateFlipExtensions.cs index 4d5d90c30e..0e4ad4066c 100644 --- a/src/ImageSharp/Processing/Extensions/RotateFlipExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/RotateFlipExtensions.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing { /// diff --git a/src/ImageSharp/Processing/Extensions/SaturateExtensions.cs b/src/ImageSharp/Processing/Extensions/SaturateExtensions.cs index e9ba820b6c..a94a9a407d 100644 --- a/src/ImageSharp/Processing/Extensions/SaturateExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/SaturateExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Extensions/SepiaExtensions.cs b/src/ImageSharp/Processing/Extensions/SepiaExtensions.cs index 5ee5151fae..df32307f47 100644 --- a/src/ImageSharp/Processing/Extensions/SepiaExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/SepiaExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs index 8129836641..840d1c1f46 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; - using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs index 83701aa8a2..7f00d0219d 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; - using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Binarization diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index bda8767447..7a750bdd4a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index 764f4ca517..8504db1617 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 23282af36d..75acc90e0c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Convolution diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs index 9c9488fec0..ab6658f3b2 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs index a9db37a076..ca7d8895a8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs index fd73789027..6f5373fae5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs @@ -1,9 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs index ec0183dc63..da76aa971c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs index 3d5d1e7bf1..5fb32f4e62 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs @@ -1,9 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs index e0b79c2b20..e612b4bf03 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs @@ -3,8 +3,6 @@ using System; -using SixLabors.ImageSharp.Processing.Processors.Binarization; - namespace SixLabors.ImageSharp.Processing.Processors.Dithering { /// diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs index ae6d5f6f79..71d3f9c9cc 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs @@ -1,9 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// diff --git a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs index 012b10ee03..30484f0590 100644 --- a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// diff --git a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs index 922ca32330..a537b8f606 100644 --- a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// diff --git a/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs index 3f6dfde281..c912572f0e 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; namespace SixLabors.ImageSharp.Processing.Processors.Quantization diff --git a/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs index d659ecabf7..cd320a9a36 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; namespace SixLabors.ImageSharp.Processing.Processors.Quantization diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs index 9bbbba843c..3eb0d998a2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs index dce4e70d62..14bf552b9d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs @@ -4,7 +4,6 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs index 4b81aaa64e..6d6e22a6a5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; - using SixLabors.Memory; namespace SixLabors.ImageSharp.Processing.Processors.Transforms diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index fb03057a48..91e9a91076 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -2,12 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.Collections.Generic; using System.Linq; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index cb73bb66c2..4b87d6d2c1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -3,7 +3,6 @@ using System.Numerics; -using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs index ef508549ac..f6d3299fcd 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms diff --git a/src/ImageSharp/Properties/AssemblyInfo.cs b/src/ImageSharp/Properties/AssemblyInfo.cs index 2862b45851..225de354ae 100644 --- a/src/ImageSharp/Properties/AssemblyInfo.cs +++ b/src/ImageSharp/Properties/AssemblyInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Runtime.CompilerServices; - // Redundant suppressing of SA1413 for Rider. [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage( From f663adf9bdddb2f78db2f5a1b23ab5cbdec7ffae Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 5 Aug 2019 14:58:16 +0200 Subject: [PATCH 77/82] Minor code refactoring --- .../Processors/Convolution/BokehBlurProcessor{TPixel}.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 48ed872180..c6d55dceb7 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -517,20 +517,20 @@ private void ApplyInverseGammaExposure( for (int y = rows.Min; y < rows.Max; y++) { - Span targetRowSpan = sourceValues.GetRowSpan(y).Slice(startX); Span targetPixelSpan = targetPixels.GetRowSpan(y).Slice(startX); - ref Vector4 baseRef = ref MemoryMarshal.GetReference(targetRowSpan); + Span sourceRowSpan = sourceValues.GetRowSpan(y).Slice(startX); + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceRowSpan); for (int x = 0; x < width; x++) { - ref Vector4 v = ref Unsafe.Add(ref baseRef, x); + ref Vector4 v = ref Unsafe.Add(ref sourceRef, x); var clamp = Vector4.Clamp(v, low, high); v.X = MathF.Pow(clamp.X, expGamma); v.Y = MathF.Pow(clamp.Y, expGamma); v.Z = MathF.Pow(clamp.Z, expGamma); } - PixelOperations.Instance.FromVector4Destructive(configuration, targetRowSpan.Slice(0, width), targetPixelSpan, PixelConversionModifiers.Premultiply); + PixelOperations.Instance.FromVector4Destructive(configuration, sourceRowSpan.Slice(0, width), targetPixelSpan, PixelConversionModifiers.Premultiply); } }); } From 8e9986e058b07d44a5ddf9990931ad335ced35a9 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 5 Aug 2019 15:04:01 +0200 Subject: [PATCH 78/82] More minor code refactoring --- .../Processors/Convolution/BokehBlurProcessor{TPixel}.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index c6d55dceb7..b9780250ed 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -454,7 +454,7 @@ private void ApplyGammaExposure( var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); int width = workingRectangle.Width; - float gamma = this.gamma; + float exp = this.gamma; ParallelHelper.IterateRowsWithTempBuffer( workingRectangle, @@ -473,9 +473,9 @@ private void ApplyGammaExposure( for (int x = 0; x < width; x++) { ref Vector4 v = ref Unsafe.Add(ref baseRef, x); - v.X = MathF.Pow(v.X, gamma); - v.Y = MathF.Pow(v.Y, gamma); - v.Z = MathF.Pow(v.Z, gamma); + v.X = MathF.Pow(v.X, exp); + v.Y = MathF.Pow(v.Y, exp); + v.Z = MathF.Pow(v.Z, exp); } PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan); From 5d28f6e25615da87d50df84a6342548ce15f390e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Aug 2019 22:54:35 +1000 Subject: [PATCH 79/82] Update External --- tests/Images/External | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Images/External b/tests/Images/External index acc32594c1..36f39bc624 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit acc32594c125656840f8a17e69b0ebb49a370fa6 +Subproject commit 36f39bc624f8a49caf512077bf70cab30c2e5fb4 From c94948c3501d6b60f1b1a5aefd653479f037ae67 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Aug 2019 23:17:05 +1000 Subject: [PATCH 80/82] Fix undisposed buffers --- .../Convolution/BokehBlurProcessor{TPixel}.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index b9780250ed..85dac715e9 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -248,8 +248,8 @@ private void NormalizeKernels() ref Complex64 jRef = ref Unsafe.Add(ref valueRef, j); ref Complex64 kRef = ref Unsafe.Add(ref valueRef, k); total += - (paramsRef.Z * ((jRef.Real * kRef.Real) - (jRef.Imaginary * kRef.Imaginary))) + - (paramsRef.W * ((jRef.Real * kRef.Imaginary) + (jRef.Imaginary * kRef.Real))); + (paramsRef.Z * ((jRef.Real * kRef.Real) - (jRef.Imaginary * kRef.Imaginary))) + + (paramsRef.W * ((jRef.Real * kRef.Imaginary) + (jRef.Imaginary * kRef.Real))); } } } @@ -282,19 +282,17 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source { // Memory usage priority: allocate a shared buffer and execute the second convolution in sequential mode using (Buffer2D buffer = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height + this.radius)) + using (Buffer2D firstPassBuffer = buffer.Slice(this.radius, source.Height)) + using (Buffer2D secondPassBuffer = buffer.Slice(0, source.Height)) { - Buffer2D firstPassBuffer = buffer.Slice(this.radius, source.Height); - Buffer2D secondPassBuffer = buffer.Slice(0, source.Height); - this.OnFrameApplyCore(source, sourceRectangle, configuration, processing, firstPassBuffer, secondPassBuffer); } } else { // Performance priority: allocate two independent buffers and execute both convolutions in parallel mode - using (Buffer2D - firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size()), - secondPassBuffer = configuration.MemoryAllocator.Allocate2D(source.Size())) + using (Buffer2D firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) + using (Buffer2D secondPassBuffer = configuration.MemoryAllocator.Allocate2D(source.Size())) { this.OnFrameApplyCore(source, sourceRectangle, configuration, processing, firstPassValues, secondPassBuffer); } From 11fadaf1de4ef08a26ae41e1d582ef51c4c4adc9 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 6 Aug 2019 16:26:45 +0200 Subject: [PATCH 81/82] Bokeh blur processor cache switched to concurrent dictionary --- .../Processors/Convolution/BokehBlurProcessor{TPixel}.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 85dac715e9..a083026c37 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; @@ -68,7 +69,7 @@ internal class BokehBlurProcessor : ImageProcessor /// /// The mapping of initialized complex kernels and parameters, to speed up the initialization of new instances /// - private static readonly Dictionary Cache = new Dictionary(); + private static readonly ConcurrentDictionary Cache = new ConcurrentDictionary(); /// /// Initializes a new instance of the class. @@ -98,7 +99,7 @@ public BokehBlurProcessor(BokehBlurProcessor definition) this.NormalizeKernels(); // Store them in the cache for future use - Cache.Add(parameters, new BokehBlurKernelData(this.kernelParameters, this.kernelsScale, this.kernels)); + Cache.TryAdd(parameters, new BokehBlurKernelData(this.kernelParameters, this.kernelsScale, this.kernels)); } } From 871ae09ce5b6e32aabdc3a85f8ad796f59bc20a2 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 6 Aug 2019 16:27:37 +0200 Subject: [PATCH 82/82] Minor code refactoring --- src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs index b6bbd4023f..c5c9ddebe1 100644 --- a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs +++ b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs @@ -45,8 +45,6 @@ public static void Convolve2D3( int maxColumn) where TPixel : struct, IPixel { - Vector4 vector = default; - Convolve2DImpl( in matrixY, in matrixX, @@ -57,7 +55,7 @@ public static void Convolve2D3( maxRow, minColumn, maxColumn, - ref vector); + out Vector4 vector); ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column); vector.W = target.W; @@ -95,8 +93,6 @@ public static void Convolve2D4( int maxColumn) where TPixel : struct, IPixel { - Vector4 vector = default; - Convolve2DImpl( in matrixY, in matrixX, @@ -107,7 +103,7 @@ public static void Convolve2D4( maxRow, minColumn, maxColumn, - ref vector); + out Vector4 vector); ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column); Vector4Utils.UnPremultiply(ref vector); @@ -125,7 +121,7 @@ public static void Convolve2DImpl( int maxRow, int minColumn, int maxColumn, - ref Vector4 vector) + out Vector4 vector) where TPixel : struct, IPixel { Vector4 vectorY = default;