diff --git a/src/ImageSharp/Common/Constants.cs b/src/ImageSharp/Common/Constants.cs index fa2f72c74a..a3cfe3623f 100644 --- a/src/ImageSharp/Common/Constants.cs +++ b/src/ImageSharp/Common/Constants.cs @@ -1,6 +1,8 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Runtime.Intrinsics; + namespace SixLabors.ImageSharp; /// @@ -13,6 +15,11 @@ internal static class Constants /// public static readonly float Epsilon = 0.001F; + /// + /// The epsilon value for comparing floating point numbers. + /// + public static readonly Vector256 Epsilon256 = Vector256.Create(0.001F); + /// /// The epsilon squared value for comparing floating point numbers. /// diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 4bc0040c67..128218aac2 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -532,7 +532,7 @@ internal static partial class SimdUtils } /// - /// Performs a multiplication and an addition of the . + /// Performs a multiplication and an addition of the . /// /// ret = (vm0 * vm1) + va /// The vector to add to the intermediate result. @@ -549,22 +549,20 @@ internal static partial class SimdUtils { return Fma.MultiplyAdd(vm1, vm0, va); } - else - { - return Avx.Add(Avx.Multiply(vm0, vm1), va); - } + + return Avx.Add(Avx.Multiply(vm0, vm1), va); } /// - /// Performs a multiplication and a substraction of the . + /// Performs a multiplication and a subtraction of the . /// /// ret = (vm0 * vm1) - vs - /// The vector to substract from the intermediate result. + /// The vector to subtract from the intermediate result. /// The first vector to multiply. /// The second vector to multiply. /// The . [MethodImpl(InliningOptions.ShortMethod)] - public static Vector256 MultiplySubstract( + public static Vector256 MultiplySubtract( in Vector256 vs, in Vector256 vm0, in Vector256 vm1) @@ -573,10 +571,8 @@ internal static partial class SimdUtils { return Fma.MultiplySubtract(vm1, vm0, vs); } - else - { - return Avx.Subtract(Avx.Multiply(vm0, vm1), vs); - } + + return Avx.Subtract(Avx.Multiply(vm0, vm1), vs); } /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Intrinsic.cs b/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Intrinsic.cs index cae89fc3cf..7e102f696d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Intrinsic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Intrinsic.cs @@ -99,7 +99,7 @@ internal static partial class FloatingPointDCT var mm256_F_1_4142 = Vector256.Create(1.414213562f); Vector256 tmp13 = Avx.Add(tmp1, tmp3); - Vector256 tmp12 = SimdUtils.HwIntrinsics.MultiplySubstract(tmp13, Avx.Subtract(tmp1, tmp3), mm256_F_1_4142); + Vector256 tmp12 = SimdUtils.HwIntrinsics.MultiplySubtract(tmp13, Avx.Subtract(tmp1, tmp3), mm256_F_1_4142); tmp0 = Avx.Add(tmp10, tmp13); tmp3 = Avx.Subtract(tmp10, tmp13); diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index 9bc7e35f30..d7d31c0c8b 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -3,6 +3,8 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; @@ -27,9 +29,17 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Normal(Vector4 backdrop, Vector4 source) - { - return source; - } + => source; + + /// + /// Returns the result of the "Normal" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Normal(Vector256 backdrop, Vector256 source) + => source; /// /// Returns the result of the "Multiply" compositing equation. @@ -39,9 +49,17 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Multiply(Vector4 backdrop, Vector4 source) - { - return backdrop * source; - } + => backdrop * source; + + /// + /// Returns the result of the "Multiply" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Multiply(Vector256 backdrop, Vector256 source) + => Avx.Multiply(backdrop, source); /// /// Returns the result of the "Add" compositing equation. @@ -51,9 +69,17 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Add(Vector4 backdrop, Vector4 source) - { - return Vector4.Min(Vector4.One, backdrop + source); - } + => Vector4.Min(Vector4.One, backdrop + source); + + /// + /// Returns the result of the "Add" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Add(Vector256 backdrop, Vector256 source) + => Avx.Min(Vector256.Create(1F), Avx.Add(backdrop, source)); /// /// Returns the result of the "Subtract" compositing equation. @@ -63,9 +89,17 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Subtract(Vector4 backdrop, Vector4 source) - { - return Vector4.Max(Vector4.Zero, backdrop - source); - } + => Vector4.Max(Vector4.Zero, backdrop - source); + + /// + /// Returns the result of the "Subtract" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Subtract(Vector256 backdrop, Vector256 source) + => Avx.Min(Vector256.Create(1F), Avx.Subtract(backdrop, source)); /// /// Returns the result of the "Screen" compositing equation. @@ -75,8 +109,19 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Screen(Vector4 backdrop, Vector4 source) + => Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source)); + + /// + /// Returns the result of the "Screen" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Screen(Vector256 backdrop, Vector256 source) { - return Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source)); + Vector256 vOne = Vector256.Create(1F); + return Avx.Subtract(vOne, Avx.Multiply(Avx.Subtract(vOne, backdrop), Avx.Subtract(vOne, source))); } /// @@ -87,9 +132,17 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Darken(Vector4 backdrop, Vector4 source) - { - return Vector4.Min(backdrop, source); - } + => Vector4.Min(backdrop, source); + + /// + /// Returns the result of the "Darken" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Darken(Vector256 backdrop, Vector256 source) + => Avx.Min(backdrop, source); /// /// Returns the result of the "Lighten" compositing equation. @@ -98,10 +151,17 @@ internal static partial class PorterDuffFunctions /// The source vector. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Lighten(Vector4 backdrop, Vector4 source) - { - return Vector4.Max(backdrop, source); - } + public static Vector4 Lighten(Vector4 backdrop, Vector4 source) => Vector4.Max(backdrop, source); + + /// + /// Returns the result of the "Lighten" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Lighten(Vector256 backdrop, Vector256 source) + => Avx.Max(backdrop, source); /// /// Returns the result of the "Overlay" compositing equation. @@ -136,16 +196,14 @@ internal static partial class PorterDuffFunctions } /// - /// Helper function for Overlay andHardLight modes + /// Helper function for Overlay and HardLight 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)); - } + => backdrop <= 0.5f ? (2 * backdrop * source) : 1 - (2 * (1 - source) * (1 - backdrop)); /// /// Returns the result of the "Over" compositing equation. @@ -175,6 +233,40 @@ internal static partial class PorterDuffFunctions return color; } + /// + /// Returns the result of the "Over" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The amount to blend. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Over(Vector256 destination, Vector256 source, Vector256 blend) + { + const int blendAlphaControl = 0b_10_00_10_00; + const int shuffleAlphaControl = 0b_11_11_11_11; + + // calculate weights + Vector256 sW = Avx.Shuffle(source, source, shuffleAlphaControl); + Vector256 dW = Avx.Shuffle(destination, destination, shuffleAlphaControl); + Vector256 blendW = Avx.Multiply(sW, dW); + + Vector256 dstW = Avx.Subtract(dW, blendW); + Vector256 srcW = Avx.Subtract(sW, blendW); + + // calculate final alpha + Vector256 alpha = Avx.Add(dstW, sW); + + // calculate final color + Vector256 color = Avx.Multiply(destination, dstW); + color = SimdUtils.HwIntrinsics.MultiplyAdd(source, srcW, color); + color = SimdUtils.HwIntrinsics.MultiplyAdd(blend, blendW, color); + + // unpremultiply + color = Avx.Divide(color, Avx.Max(alpha, Constants.Epsilon256)); + return Avx.Blend(color, alpha, blendAlphaControl); + } + /// /// Returns the result of the "Atop" compositing equation. /// @@ -202,6 +294,36 @@ internal static partial class PorterDuffFunctions return color; } + /// + /// Returns the result of the "Atop" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The amount to blend. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Atop(Vector256 destination, Vector256 source, Vector256 blend) + { + // calculate weights + const int blendAlphaControl = 0b_10_00_10_00; + const int shuffleAlphaControl = 0b_11_11_11_11; + + // calculate final alpha + Vector256 alpha = Avx.Shuffle(destination, destination, shuffleAlphaControl); + + // calculate weights + Vector256 sW = Avx.Shuffle(source, source, shuffleAlphaControl); + Vector256 blendW = Avx.Multiply(sW, alpha); + Vector256 dstW = Avx.Subtract(alpha, blendW); + + // calculate final color + Vector256 color = SimdUtils.HwIntrinsics.MultiplyAdd(destination, dstW, Avx.Multiply(blend, blendW)); + + // unpremultiply + color = Avx.Divide(color, Avx.Max(alpha, Constants.Epsilon256)); + return Avx.Blend(color, alpha, blendAlphaControl); + } + /// /// Returns the result of the "In" compositing equation. /// @@ -220,6 +342,31 @@ internal static partial class PorterDuffFunctions return color; } + /// + /// Returns the result of the "In" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 In(Vector256 destination, Vector256 source) + { + const int blendAlphaControl = 0b_10_00_10_00; + const int shuffleAlphaControl = 0b_11_11_11_11; + + // calculate alpha + Vector256 sW = Avx.Shuffle(source, source, shuffleAlphaControl); + Vector256 dW = Avx.Shuffle(destination, destination, shuffleAlphaControl); + Vector256 alpha = Avx.Multiply(sW, dW); + + // premultiply + Vector256 color = Avx.Multiply(source, alpha); + + // unpremultiply + color = Avx.Divide(color, Avx.Max(alpha, Constants.Epsilon256)); + return Avx.Blend(color, alpha, blendAlphaControl); + } + /// /// Returns the result of the "Out" compositing equation. /// @@ -238,6 +385,31 @@ internal static partial class PorterDuffFunctions return color; } + /// + /// Returns the result of the "Out" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Out(Vector256 destination, Vector256 source) + { + const int blendAlphaControl = 0b_10_00_10_00; + const int shuffleAlphaControl = 0b_11_11_11_11; + + // calculate alpha + Vector256 sW = Avx.Shuffle(source, source, shuffleAlphaControl); + Vector256 dW = Avx.Shuffle(destination, destination, shuffleAlphaControl); + Vector256 alpha = Avx.Multiply(Avx.Subtract(Vector256.Create(1F), dW), sW); + + // premultiply + Vector256 color = Avx.Multiply(source, alpha); + + // unpremultiply + color = Avx.Divide(color, Avx.Max(alpha, Constants.Epsilon256)); + return Avx.Blend(color, alpha, blendAlphaControl); + } + /// /// Returns the result of the "XOr" compositing equation. /// @@ -260,9 +432,38 @@ internal static partial class PorterDuffFunctions return color; } + /// + /// Returns the result of the "XOr" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector4 Clear(Vector4 backdrop, Vector4 source) + public static Vector256 Xor(Vector256 destination, Vector256 source) { - return Vector4.Zero; + const int blendAlphaControl = 0b_10_00_10_00; + const int shuffleAlphaControl = 0b_11_11_11_11; + + // calculate weights + Vector256 sW = Avx.Shuffle(source, source, shuffleAlphaControl); + Vector256 dW = Avx.Shuffle(destination, destination, shuffleAlphaControl); + + Vector256 vOne = Vector256.Create(1F); + Vector256 srcW = Avx.Subtract(vOne, dW); + Vector256 dstW = Avx.Subtract(vOne, sW); + + // calculate alpha + Vector256 alpha = SimdUtils.HwIntrinsics.MultiplyAdd(sW, srcW, Avx.Multiply(dW, dstW)); + Vector256 color = SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(sW, source), srcW, Avx.Multiply(Avx.Multiply(dW, destination), dstW)); + + // unpremultiply + color = Avx.Divide(color, Avx.Max(alpha, Constants.Epsilon256)); + return Avx.Blend(color, alpha, blendAlphaControl); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Clear(Vector4 backdrop, Vector4 source) => Vector4.Zero; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 Clear(Vector256 backdrop, Vector256 source) => Vector256.Zero; }