diff --git a/.gitignore b/.gitignore
index fadf36964c..a8d2917be7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -227,3 +227,5 @@ artifacts/
#lfs
hooks/**
lfs/**
+
+.dotnet
diff --git a/src/ImageSharp/Formats/Png/Chunks/FrameControl.cs b/src/ImageSharp/Formats/Png/Chunks/FrameControl.cs
index 91f79c8154..0038b41344 100644
--- a/src/ImageSharp/Formats/Png/Chunks/FrameControl.cs
+++ b/src/ImageSharp/Formats/Png/Chunks/FrameControl.cs
@@ -147,7 +147,13 @@ internal readonly struct FrameControl
/// The data to parse.
/// The parsed fcTL.
public static FrameControl Parse(ReadOnlySpan data)
- => new(
+ {
+ if (data.Length < Size)
+ {
+ PngThrowHelper.ThrowInvalidImageContentException("The frame control chunk does not contain enough data!");
+ }
+
+ return new(
sequenceNumber: BinaryPrimitives.ReadUInt32BigEndian(data[..4]),
width: BinaryPrimitives.ReadUInt32BigEndian(data[4..8]),
height: BinaryPrimitives.ReadUInt32BigEndian(data[8..12]),
@@ -157,4 +163,5 @@ internal readonly struct FrameControl
delayDenominator: BinaryPrimitives.ReadUInt16BigEndian(data[22..24]),
disposalMode: (FrameDisposalMode)(data[24] + 1),
blendMode: (FrameBlendMode)data[25]);
+ }
}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs
index f62d3c6761..7cd9cc57ad 100644
--- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs
@@ -81,8 +81,10 @@ internal static class DefaultPixelBlenders
}
///
- protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount)
+ protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount)
{
+ amount = Numerics.Clamp(amount, 0, 1);
+
if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256
@@ -90,65 +92,35 @@ internal static class DefaultPixelBlenders
ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background));
- ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
- ref float amountBase = ref MemoryMarshal.GetReference(amount);
-
- Vector256 vOne = Vector256.Create(1F);
+ Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
+ Vector256 opacity = Vector256.Create(amount);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
- // We need to create a Vector256 containing the current and next amount values
- // taking up each half of the Vector256 and then clamp them.
- Vector256 opacity = Vector256.Create(
- Vector128.Create(amountBase),
- Vector128.Create(Unsafe.Add(ref amountBase, 1)));
- opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne);
-
destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
- sourceBase = ref Unsafe.Add(ref sourceBase, 1);
- amountBase = ref Unsafe.Add(ref amountBase, 2);
}
if (Numerics.Modulo2(destination.Length) != 0)
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.NormalSrc(background[i], source, amount);
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.NormalSrc(background[i], source, amount);
}
}
}
- }
-
- ///
- /// A pixel blender that implements the "MultiplySrc" composition equation.
- ///
- public class MultiplySrc : PixelBlender
- {
- ///
- /// Gets the static instance of this blender.
- ///
- public static MultiplySrc Instance { get; } = new MultiplySrc();
-
- ///
- public override TPixel Blend(TPixel background, TPixel source, float amount)
- {
- return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
- }
///
- protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount)
+ protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount)
{
- amount = Numerics.Clamp(amount, 0, 1);
-
if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256
@@ -157,34 +129,44 @@ internal static class DefaultPixelBlenders
ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background));
ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
- Vector256 opacity = Vector256.Create(amount);
+ ref float amountBase = ref MemoryMarshal.GetReference(amount);
+
+ Vector256 vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
- destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity);
+ // We need to create a Vector256 containing the current and next amount values
+ // taking up each half of the Vector256 and then clamp them.
+ Vector256 opacity = Vector256.Create(
+ Vector128.Create(amountBase),
+ Vector128.Create(Unsafe.Add(ref amountBase, 1)));
+ opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne);
+
+ destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
sourceBase = ref Unsafe.Add(ref sourceBase, 1);
+ amountBase = ref Unsafe.Add(ref amountBase, 2);
}
if (Numerics.Modulo2(destination.Length) != 0)
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
}
}
}
///
- protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount)
+ protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount)
{
if (Avx2.IsSupported && destination.Length >= 2)
{
@@ -193,9 +175,9 @@ internal static class DefaultPixelBlenders
ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background));
- ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
ref float amountBase = ref MemoryMarshal.GetReference(amount);
+ Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
Vector256 vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
@@ -207,10 +189,9 @@ internal static class DefaultPixelBlenders
Vector128.Create(Unsafe.Add(ref amountBase, 1)));
opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne);
- destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity);
+ destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
- sourceBase = ref Unsafe.Add(ref sourceBase, 1);
amountBase = ref Unsafe.Add(ref amountBase, 2);
}
@@ -218,33 +199,33 @@ internal static class DefaultPixelBlenders
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.NormalSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.NormalSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
}
}
}
}
///
- /// A pixel blender that implements the "AddSrc" composition equation.
+ /// A pixel blender that implements the "MultiplySrc" composition equation.
///
- public class AddSrc : PixelBlender
+ public class MultiplySrc : PixelBlender
{
///
/// Gets the static instance of this blender.
///
- public static AddSrc Instance { get; } = new AddSrc();
+ public static MultiplySrc Instance { get; } = new MultiplySrc();
///
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
- return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
+ return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
///
@@ -264,7 +245,7 @@ internal static class DefaultPixelBlenders
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
- destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity);
+ destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
sourceBase = ref Unsafe.Add(ref sourceBase, 1);
@@ -274,21 +255,23 @@ internal static class DefaultPixelBlenders
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount);
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount);
}
}
}
///
- protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount)
+ protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount)
{
+ amount = Numerics.Clamp(amount, 0, 1);
+
if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256
@@ -296,65 +279,35 @@ internal static class DefaultPixelBlenders
ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background));
- ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
- ref float amountBase = ref MemoryMarshal.GetReference(amount);
-
- Vector256 vOne = Vector256.Create(1F);
+ Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
+ Vector256 opacity = Vector256.Create(amount);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
- // We need to create a Vector256 containing the current and next amount values
- // taking up each half of the Vector256 and then clamp them.
- Vector256 opacity = Vector256.Create(
- Vector128.Create(amountBase),
- Vector128.Create(Unsafe.Add(ref amountBase, 1)));
- opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne);
-
- destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity);
+ destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
- sourceBase = ref Unsafe.Add(ref sourceBase, 1);
- amountBase = ref Unsafe.Add(ref amountBase, 2);
}
if (Numerics.Modulo2(destination.Length) != 0)
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, amount);
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, amount);
}
}
}
- }
-
- ///
- /// A pixel blender that implements the "SubtractSrc" composition equation.
- ///
- public class SubtractSrc : PixelBlender
- {
- ///
- /// Gets the static instance of this blender.
- ///
- public static SubtractSrc Instance { get; } = new SubtractSrc();
-
- ///
- public override TPixel Blend(TPixel background, TPixel source, float amount)
- {
- return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
- }
///
- protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount)
+ protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount)
{
- amount = Numerics.Clamp(amount, 0, 1);
-
if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256
@@ -363,34 +316,44 @@ internal static class DefaultPixelBlenders
ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background));
ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
- Vector256 opacity = Vector256.Create(amount);
+ ref float amountBase = ref MemoryMarshal.GetReference(amount);
+
+ Vector256 vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
- destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity);
+ // We need to create a Vector256 containing the current and next amount values
+ // taking up each half of the Vector256 and then clamp them.
+ Vector256 opacity = Vector256.Create(
+ Vector128.Create(amountBase),
+ Vector128.Create(Unsafe.Add(ref amountBase, 1)));
+ opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne);
+
+ destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
sourceBase = ref Unsafe.Add(ref sourceBase, 1);
+ amountBase = ref Unsafe.Add(ref amountBase, 2);
}
if (Numerics.Modulo2(destination.Length) != 0)
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
}
}
}
///
- protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount)
+ protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount)
{
if (Avx2.IsSupported && destination.Length >= 2)
{
@@ -399,9 +362,9 @@ internal static class DefaultPixelBlenders
ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background));
- ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
ref float amountBase = ref MemoryMarshal.GetReference(amount);
+ Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
Vector256 vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
@@ -413,10 +376,9 @@ internal static class DefaultPixelBlenders
Vector128.Create(Unsafe.Add(ref amountBase, 1)));
opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne);
- destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity);
+ destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
- sourceBase = ref Unsafe.Add(ref sourceBase, 1);
amountBase = ref Unsafe.Add(ref amountBase, 2);
}
@@ -424,33 +386,33 @@ internal static class DefaultPixelBlenders
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
}
}
}
}
///
- /// A pixel blender that implements the "ScreenSrc" composition equation.
+ /// A pixel blender that implements the "AddSrc" composition equation.
///
- public class ScreenSrc : PixelBlender
+ public class AddSrc : PixelBlender
{
///
/// Gets the static instance of this blender.
///
- public static ScreenSrc Instance { get; } = new ScreenSrc();
+ public static AddSrc Instance { get; } = new AddSrc();
///
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
- return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
+ return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
///
@@ -470,7 +432,7 @@ internal static class DefaultPixelBlenders
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
- destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity);
+ destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
sourceBase = ref Unsafe.Add(ref sourceBase, 1);
@@ -480,21 +442,23 @@ internal static class DefaultPixelBlenders
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount);
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount);
}
}
}
///
- protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount)
+ protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount)
{
+ amount = Numerics.Clamp(amount, 0, 1);
+
if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256
@@ -502,65 +466,35 @@ internal static class DefaultPixelBlenders
ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background));
- ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
- ref float amountBase = ref MemoryMarshal.GetReference(amount);
-
- Vector256 vOne = Vector256.Create(1F);
+ Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
+ Vector256 opacity = Vector256.Create(amount);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
- // We need to create a Vector256 containing the current and next amount values
- // taking up each half of the Vector256 and then clamp them.
- Vector256 opacity = Vector256.Create(
- Vector128.Create(amountBase),
- Vector128.Create(Unsafe.Add(ref amountBase, 1)));
- opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne);
-
- destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity);
+ destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
- sourceBase = ref Unsafe.Add(ref sourceBase, 1);
- amountBase = ref Unsafe.Add(ref amountBase, 2);
}
if (Numerics.Modulo2(destination.Length) != 0)
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.AddSrc(background[i], source, amount);
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.AddSrc(background[i], source, amount);
}
}
}
- }
-
- ///
- /// A pixel blender that implements the "DarkenSrc" composition equation.
- ///
- public class DarkenSrc : PixelBlender
- {
- ///
- /// Gets the static instance of this blender.
- ///
- public static DarkenSrc Instance { get; } = new DarkenSrc();
-
- ///
- public override TPixel Blend(TPixel background, TPixel source, float amount)
- {
- return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
- }
///
- protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount)
+ protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount)
{
- amount = Numerics.Clamp(amount, 0, 1);
-
if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256
@@ -569,34 +503,44 @@ internal static class DefaultPixelBlenders
ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background));
ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
- Vector256 opacity = Vector256.Create(amount);
+ ref float amountBase = ref MemoryMarshal.GetReference(amount);
+
+ Vector256 vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
- destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity);
+ // We need to create a Vector256 containing the current and next amount values
+ // taking up each half of the Vector256 and then clamp them.
+ Vector256 opacity = Vector256.Create(
+ Vector128.Create(amountBase),
+ Vector128.Create(Unsafe.Add(ref amountBase, 1)));
+ opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne);
+
+ destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
sourceBase = ref Unsafe.Add(ref sourceBase, 1);
+ amountBase = ref Unsafe.Add(ref amountBase, 2);
}
if (Numerics.Modulo2(destination.Length) != 0)
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
}
}
}
///
- protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount)
+ protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount)
{
if (Avx2.IsSupported && destination.Length >= 2)
{
@@ -605,9 +549,9 @@ internal static class DefaultPixelBlenders
ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background));
- ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
ref float amountBase = ref MemoryMarshal.GetReference(amount);
+ Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
Vector256 vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
@@ -619,10 +563,9 @@ internal static class DefaultPixelBlenders
Vector128.Create(Unsafe.Add(ref amountBase, 1)));
opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne);
- destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity);
+ destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
- sourceBase = ref Unsafe.Add(ref sourceBase, 1);
amountBase = ref Unsafe.Add(ref amountBase, 2);
}
@@ -630,33 +573,33 @@ internal static class DefaultPixelBlenders
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.AddSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.AddSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
}
}
}
}
///
- /// A pixel blender that implements the "LightenSrc" composition equation.
+ /// A pixel blender that implements the "SubtractSrc" composition equation.
///
- public class LightenSrc : PixelBlender
+ public class SubtractSrc : PixelBlender
{
///
/// Gets the static instance of this blender.
///
- public static LightenSrc Instance { get; } = new LightenSrc();
+ public static SubtractSrc Instance { get; } = new SubtractSrc();
///
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
- return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
+ return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
///
@@ -676,7 +619,7 @@ internal static class DefaultPixelBlenders
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
- destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity);
+ destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
sourceBase = ref Unsafe.Add(ref sourceBase, 1);
@@ -686,21 +629,23 @@ internal static class DefaultPixelBlenders
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount);
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount);
}
}
}
///
- protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount)
+ protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount)
{
+ amount = Numerics.Clamp(amount, 0, 1);
+
if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256
@@ -708,65 +653,35 @@ internal static class DefaultPixelBlenders
ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background));
- ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
- ref float amountBase = ref MemoryMarshal.GetReference(amount);
-
- Vector256 vOne = Vector256.Create(1F);
+ Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
+ Vector256 opacity = Vector256.Create(amount);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
- // We need to create a Vector256 containing the current and next amount values
- // taking up each half of the Vector256 and then clamp them.
- Vector256 opacity = Vector256.Create(
- Vector128.Create(amountBase),
- Vector128.Create(Unsafe.Add(ref amountBase, 1)));
- opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne);
-
- destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity);
+ destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
- sourceBase = ref Unsafe.Add(ref sourceBase, 1);
- amountBase = ref Unsafe.Add(ref amountBase, 2);
}
if (Numerics.Modulo2(destination.Length) != 0)
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, amount);
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, amount);
}
}
}
- }
-
- ///
- /// A pixel blender that implements the "OverlaySrc" composition equation.
- ///
- public class OverlaySrc : PixelBlender
- {
- ///
- /// Gets the static instance of this blender.
- ///
- public static OverlaySrc Instance { get; } = new OverlaySrc();
-
- ///
- public override TPixel Blend(TPixel background, TPixel source, float amount)
- {
- return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
- }
///
- protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount)
+ protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount)
{
- amount = Numerics.Clamp(amount, 0, 1);
-
if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256
@@ -775,34 +690,44 @@ internal static class DefaultPixelBlenders
ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background));
ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
- Vector256 opacity = Vector256.Create(amount);
+ ref float amountBase = ref MemoryMarshal.GetReference(amount);
+
+ Vector256 vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
- destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity);
+ // We need to create a Vector256 containing the current and next amount values
+ // taking up each half of the Vector256 and then clamp them.
+ Vector256 opacity = Vector256.Create(
+ Vector128.Create(amountBase),
+ Vector128.Create(Unsafe.Add(ref amountBase, 1)));
+ opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne);
+
+ destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
sourceBase = ref Unsafe.Add(ref sourceBase, 1);
+ amountBase = ref Unsafe.Add(ref amountBase, 2);
}
if (Numerics.Modulo2(destination.Length) != 0)
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
}
}
}
///
- protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount)
+ protected override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount)
{
if (Avx2.IsSupported && destination.Length >= 2)
{
@@ -811,9 +736,9 @@ internal static class DefaultPixelBlenders
ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background));
- ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
ref float amountBase = ref MemoryMarshal.GetReference(amount);
+ Vector256 sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
Vector256 vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
@@ -825,10 +750,9 @@ internal static class DefaultPixelBlenders
Vector128.Create(Unsafe.Add(ref amountBase, 1)));
opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne);
- destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity);
+ destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
- sourceBase = ref Unsafe.Add(ref sourceBase, 1);
amountBase = ref Unsafe.Add(ref amountBase, 2);
}
@@ -836,33 +760,33 @@ internal static class DefaultPixelBlenders
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
+ destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
}
}
}
}
///
- /// A pixel blender that implements the "HardLightSrc" composition equation.
+ /// A pixel blender that implements the "ScreenSrc" composition equation.
///
- public class HardLightSrc : PixelBlender
+ public class ScreenSrc : PixelBlender
{
///
/// Gets the static instance of this blender.
///
- public static HardLightSrc Instance { get; } = new HardLightSrc();
+ public static ScreenSrc Instance { get; } = new ScreenSrc();
///
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
- return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
+ return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
///
@@ -882,7 +806,7 @@ internal static class DefaultPixelBlenders
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
- destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity);
+ destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
sourceBase = ref Unsafe.Add(ref sourceBase, 1);
@@ -892,21 +816,23 @@ internal static class DefaultPixelBlenders
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
int i = destination.Length - 1;
- destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount);
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
- destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount);
+ destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount);
}
}
}
///
- protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount)
+ protected override void BlendFunction(Span