Browse Source

Changed Pixel Blender/Composer generators to generate all combinations of ColorBlenders and AlphaComposers

pull/641/head
Vicente Penades 8 years ago
parent
commit
be1e555a60
  1. 2
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/GradientBrushBase{TPixel}.cs
  2. 3679
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs
  3. 50
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt
  4. 2827
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
  5. 150
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt
  6. 371
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs
  7. 42
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs
  8. 4
      tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs
  9. 18
      tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs
  10. 54
      tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs
  11. 84
      tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs

2
src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/GradientBrushBase{TPixel}.cs

@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / to.Ratio; float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / to.Ratio;
// TODO: this should be changeble for different gradienting functions // TODO: this should be changeble for different gradienting functions
Vector4 result = PorterDuffFunctions.Normal( Vector4 result = PorterDuffFunctions.Normal_SrcOver(
fromAsVector, fromAsVector,
toAsVector, toAsVector,
onLocalGradient); onLocalGradient);

3679
src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs

File diff suppressed because it is too large

50
src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{ {
using System; using System;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Memory; using SixLabors.Memory;
/// <summary> /// <summary>
@ -35,24 +35,12 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{ {
<# <#
string[] composers = new []{
string[] blenders = new []{
"Normal",
"Multiply",
"Add",
"Subtract",
"Screen",
"Darken",
"Lighten",
"Overlay",
"HardLight",
"Src" , "Src" ,
"Atop" , "SrcAtop" ,
"Over" , "SrcOver" ,
"In" , "SrcIn" ,
"Out" , "SrcOut" ,
"Dest" , "Dest" ,
"DestAtop" , "DestAtop" ,
"DestOver" , "DestOver" ,
@ -62,21 +50,37 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
"Xor" , "Xor" ,
}; };
string[] blenders = new []{
"Normal" ,
"Multiply" ,
"Add" ,
"Subtract" ,
"Screen" ,
"Darken" ,
"Lighten" ,
"Overlay" ,
"HardLight"
};
foreach(var composer in composers) {
foreach(var blender in blenders) { foreach(var blender in blenders) {
string blender_composer= $"{blender}_{composer}";
#> #>
internal class <#=blender#> : PixelBlender<TPixel> internal class <#= blender_composer#> : PixelBlender<TPixel>
{ {
/// <summary> /// <summary>
/// Gets the static instance of this blender. /// Gets the static instance of this blender.
/// </summary> /// </summary>
public static <#=blender#> Instance { get; } = new <#=blender#>(); public static <#= blender_composer#> Instance { get; } = new <#= blender_composer#>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount) public override TPixel Blend(TPixel background, TPixel source, float amount)
{ {
return PorterDuffFunctions.<#=blender#>(background, source, amount); return PorterDuffFunctions.<#= blender_composer#>(background, source, amount);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -97,7 +101,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
destinationSpan[i] = PorterDuffFunctions.<#=blender#>(backgroundSpan[i], sourceSpan[i], amount[i]); destinationSpan[i] = PorterDuffFunctions.<#= blender_composer#>(backgroundSpan[i], sourceSpan[i], amount[i]);
} }
PixelOperations<TPixel>.Instance.PackFromVector4(destinationSpan, destination, destination.Length); PixelOperations<TPixel>.Instance.PackFromVector4(destinationSpan, destination, destination.Length);
@ -106,7 +110,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
} }
<# <#
}
} }
#> #>

2827
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs

File diff suppressed because it is too large

150
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt

@ -21,37 +21,15 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{ {
internal static partial class PorterDuffFunctions internal static partial class PorterDuffFunctions
{ {
<# <# void GenerateVectorCompositor(string name, string sourceVar, string destVar, string blendVar)
void GeneratePixelBlender (string blender)
{
#>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel <#=blender#><TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default;
dest.PackFromVector4(<#=blender#>(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
<#
}
void GenerateVectorCompositor(string name, string sourceVar, string destVar, string blendVar)
{ {
int a_s = sourceVar == "Vector4.Zero" ? 0 : 1; int a_s = sourceVar == "Vector4.Zero" ? 0 : 1;
int a_b = destVar == "Vector4.Zero" ? 0 : 1; int a_b = destVar == "Vector4.Zero" ? 0 : 1;
int a_x = blendVar == "Vector4.Zero" ? 0 : 1; int a_x = blendVar == "Vector4.Zero" ? 0 : 1;
#> #>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=name#>(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=name#>(Vector4 backdrop, Vector4 source, Vector4 xform)
{ {
opacity = opacity.Clamp(0, 1);
<# if(sourceVar != "Vector4.Zero" ) { #>
source.W *= opacity;
<# } #>
// calculate weights // calculate weights
float xw = backdrop.W * source.W; float xw = backdrop.W * source.W;
float bw = backdrop.W - xw; float bw = backdrop.W - xw;
@ -61,52 +39,98 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
float fw = (sw * <#=a_s#>) + (bw * <#=a_b#>) + (xw * <#=a_x#>); float fw = (sw * <#=a_s#>) + (bw * <#=a_b#>) + (xw * <#=a_x#>);
// calculate final value // calculate final value
Vector4 xform = ((<#=blendVar#> * xw) + (<#=destVar#> * bw) + (<#=sourceVar#> * sw)) / MathF.Max(fw, Constants.Epsilon); xform = ((<#=blendVar#> * xw) + (<#=destVar#> * bw) + (<#=sourceVar#> * sw)) / MathF.Max(fw, Constants.Epsilon);
xform.W = fw; xform.W = fw;
return Vector4.Lerp(backdrop, xform, opacity); return xform;
}
<# } #>
<# void GeneratePixelBlender(string blender, string compositor) { #>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>_<#=compositor#>(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
return <#=compositor#>(backdrop,source, <#=blender#>(backdrop, source));
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel <#=blender#>_<#=compositor#><TPixel>(TPixel backdrop, TPixel source, float opacity)
where TPixel : struct, IPixel<TPixel>
{
opacity = opacity.Clamp(0, 1);
Vector4 backdropV = backdrop.ToVector4();
Vector4 sourceV = source.ToVector4();
sourceV.W *= opacity;
TPixel dest = default;
dest.PackFromVector4(<#=compositor#>(backdropV,sourceV, <#=blender#>(backdropV, sourceV)));
return dest;
}
<# } #>
#region Compositors
<# <#
} GenerateVectorCompositor("Src", "source", "Vector4.Zero", "xform");
GenerateVectorCompositor("Src", "source", "Vector4.Zero", "source"); GenerateVectorCompositor("SrcAtop", "Vector4.Zero", "backdrop", "xform");
GenerateVectorCompositor("Atop", "Vector4.Zero", "backdrop", "source"); GenerateVectorCompositor("SrcOver", "source", "backdrop", "xform");
GenerateVectorCompositor("Over", "source", "backdrop", "source"); GenerateVectorCompositor("SrcIn", "Vector4.Zero", "Vector4.Zero", "xform");
GenerateVectorCompositor("In", "Vector4.Zero", "Vector4.Zero", "source"); GenerateVectorCompositor("SrcOut", "source", "Vector4.Zero", "Vector4.Zero");
GenerateVectorCompositor("Out", "source", "Vector4.Zero", "Vector4.Zero"); GenerateVectorCompositor("Dest", "Vector4.Zero", "backdrop", "backdrop");
GenerateVectorCompositor("Dest", "Vector4.Zero", "backdrop", "backdrop"); GenerateVectorCompositor("DestAtop", "source", "Vector4.Zero", "backdrop");
GenerateVectorCompositor("DestAtop", "source", "Vector4.Zero", "backdrop"); GenerateVectorCompositor("DestOver", "source", "backdrop", "backdrop");
GenerateVectorCompositor("DestOver", "source", "backdrop", "backdrop"); GenerateVectorCompositor("DestIn", "Vector4.Zero", "Vector4.Zero", "backdrop");
GenerateVectorCompositor("DestIn", "Vector4.Zero", "Vector4.Zero", "backdrop"); GenerateVectorCompositor("DestOut", "Vector4.Zero", "backdrop", "Vector4.Zero");
GenerateVectorCompositor("DestOut", "Vector4.Zero", "backdrop", "Vector4.Zero"); GenerateVectorCompositor("Clear", "Vector4.Zero", "Vector4.Zero", "Vector4.Zero");
GenerateVectorCompositor("Clear", "Vector4.Zero", "Vector4.Zero", "Vector4.Zero"); GenerateVectorCompositor("Xor", "source", "backdrop", "Vector4.Zero");
GenerateVectorCompositor("Xor", "source", "backdrop", "Vector4.Zero"); #>
#endregion
GeneratePixelBlender("Normal");
GeneratePixelBlender("Multiply"); #region Blenders
GeneratePixelBlender("Add");
GeneratePixelBlender("Subtract"); <#
GeneratePixelBlender("Screen"); string[] composers = new []{
GeneratePixelBlender("Darken"); "Src" ,
GeneratePixelBlender("Lighten"); "SrcAtop" ,
GeneratePixelBlender("Overlay"); "SrcOver" ,
GeneratePixelBlender("HardLight"); "SrcIn" ,
"SrcOut" ,
GeneratePixelBlender("Src"); "Dest" ,
GeneratePixelBlender("Atop"); "DestAtop" ,
GeneratePixelBlender("Over"); "DestOver" ,
GeneratePixelBlender("In"); "DestIn" ,
GeneratePixelBlender("Out"); "DestOut" ,
GeneratePixelBlender("Dest"); "Clear" ,
GeneratePixelBlender("DestAtop"); "Xor" ,
GeneratePixelBlender("DestOver"); };
GeneratePixelBlender("DestIn");
GeneratePixelBlender("DestOut");
GeneratePixelBlender("Clear");
GeneratePixelBlender("Xor");
string[] blenders = new []{
"Normal" ,
"Multiply" ,
"Add" ,
"Subtract" ,
"Screen" ,
"Darken" ,
"Lighten" ,
"Overlay" ,
"HardLight"
};
foreach(var composer in composers)
{
foreach(var blender in blenders)
{
GeneratePixelBlender(blender,composer);
}
}
#> #>
#endregion
} }
} }

371
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs

@ -1,194 +1,179 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{ {
/// <summary> /// <summary>
/// Collection of Porter Duff alpha blending functions applying an the 'Over' composition model. /// Collection of Porter Duff alpha blending functions applying an the 'Over' composition model.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// These functions are designed to be a general solution for all color cases, /// 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 /// 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 /// and source, and there's no need to alpha-premultiply neither the backdrop
/// nor the source. /// nor the source.
/// Note there are faster functions for when the backdrop color is known /// Note there are faster functions for when the backdrop color is known
/// to be opaque /// to be opaque
/// </remarks> /// </remarks>
internal static partial class PorterDuffFunctions internal static partial class PorterDuffFunctions
{ {
/// <summary> /// <summary>
/// Source over backdrop /// Source over backdrop
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <returns>Output color</returns>
/// <returns>Output color</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Normal(Vector4 backdrop, Vector4 source)
public static Vector4 Normal(Vector4 backdrop, Vector4 source, float opacity) {
{ return source;
source.W *= opacity; }
return Compose(backdrop, source, source);
} /// <summary>
/// Source multiplied by backdrop
/// <summary> /// </summary>
/// Source multiplied by backdrop /// <param name="backdrop">Backdrop color</param>
/// </summary> /// <param name="source">Source color</param>
/// <param name="backdrop">Backdrop color</param> /// <returns>Output color</returns>
/// <param name="source">Source color</param> [MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <param name="opacity">Opacity applied to Source Alpha</param> public static Vector4 Multiply(Vector4 backdrop, Vector4 source)
/// <returns>Output color</returns> {
[MethodImpl(MethodImplOptions.AggressiveInlining)] return backdrop * source;
public static Vector4 Multiply(Vector4 backdrop, Vector4 source, float opacity) }
{
source.W *= opacity; /// <summary>
return Compose(backdrop, source, backdrop * source); /// Source added to backdrop
} /// </summary>
/// <param name="backdrop">Backdrop color</param>
/// <summary> /// <param name="source">Source color</param>
/// Source added to backdrop /// <returns>Output color</returns>
/// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <param name="backdrop">Backdrop color</param> public static Vector4 Add(Vector4 backdrop, Vector4 source)
/// <param name="source">Source color</param> {
/// <param name="opacity">Opacity applied to Source Alpha</param> return Vector4.Min(Vector4.One, backdrop + source);
/// <returns>Output color</returns> }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Add(Vector4 backdrop, Vector4 source, float opacity) /// <summary>
{ /// Source subtracted from backdrop
source.W *= opacity; /// </summary>
return Compose(backdrop, source, Vector4.Min(Vector4.One, backdrop + source)); /// <param name="backdrop">Backdrop color</param>
} /// <param name="source">Source color</param>
/// <returns>Output color</returns>
/// <summary> [MethodImpl(MethodImplOptions.AggressiveInlining)]
/// Source subtracted from backdrop public static Vector4 Subtract(Vector4 backdrop, Vector4 source)
/// </summary> {
/// <param name="backdrop">Backdrop color</param> return Vector4.Max(Vector4.Zero, backdrop - source);
/// <param name="source">Source color</param> }
/// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns> /// <summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Complement of source multiplied by the complement of backdrop
public static Vector4 Subtract(Vector4 backdrop, Vector4 source, float opacity) /// </summary>
{ /// <param name="backdrop">Backdrop color</param>
source.W *= opacity; /// <param name="source">Source color</param>
return Compose(backdrop, source, Vector4.Max(Vector4.Zero, backdrop - source)); /// <returns>Output color</returns>
} [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Screen(Vector4 backdrop, Vector4 source)
/// <summary> {
/// Complement of source multiplied by the complement of backdrop return Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source));
/// </summary> }
/// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <summary>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// Per element, chooses the smallest value of source and backdrop
/// <returns>Output color</returns> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// <param name="backdrop">Backdrop color</param>
public static Vector4 Screen(Vector4 backdrop, Vector4 source, float opacity) /// <param name="source">Source color</param>
{ /// <returns>Output color</returns>
source.W *= opacity; [MethodImpl(MethodImplOptions.AggressiveInlining)]
return Compose(backdrop, source, Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source))); public static Vector4 Darken(Vector4 backdrop, Vector4 source)
} {
return Vector4.Min(backdrop, source);
/// <summary> }
/// Per element, chooses the smallest value of source and backdrop
/// </summary> /// <summary>
/// <param name="backdrop">Backdrop color</param> /// Per element, chooses the largest value of source and backdrop
/// <param name="source">Source color</param> /// </summary>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <param name="backdrop">Backdrop color</param>
/// <returns>Output color</returns> /// <param name="source">Source color</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// <returns>Output color</returns>
public static Vector4 Darken(Vector4 backdrop, Vector4 source, float opacity) [MethodImpl(MethodImplOptions.AggressiveInlining)]
{ public static Vector4 Lighten(Vector4 backdrop, Vector4 source)
source.W *= opacity; {
return Compose(backdrop, source, Vector4.Min(backdrop, source)); return Vector4.Max(backdrop, source);
} }
/// <summary> /// <summary>
/// Per element, chooses the largest value of source and backdrop /// Overlays source over backdrop
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <returns>Output color</returns>
/// <returns>Output color</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Overlay(Vector4 backdrop, Vector4 source)
public static Vector4 Lighten(Vector4 backdrop, Vector4 source, float opacity) {
{ float cr = OverlayValueFunction(backdrop.X, source.X);
source.W *= opacity; float cg = OverlayValueFunction(backdrop.Y, source.Y);
return Compose(backdrop, source, Vector4.Max(backdrop, source)); float cb = OverlayValueFunction(backdrop.Z, source.Z);
}
return Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0));
/// <summary> }
/// Overlays source over backdrop
/// </summary> /// <summary>
/// <param name="backdrop">Backdrop color</param> /// Hard light effect
/// <param name="source">Source color</param> /// </summary>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <param name="backdrop">Backdrop color</param>
/// <returns>Output color</returns> /// <param name="source">Source color</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// <returns>Output color</returns>
public static Vector4 Overlay(Vector4 backdrop, Vector4 source, float opacity) [MethodImpl(MethodImplOptions.AggressiveInlining)]
{ public static Vector4 HardLight(Vector4 backdrop, Vector4 source)
source.W *= opacity; {
float cr = OverlayValueFunction(backdrop.X, source.X); float cr = OverlayValueFunction(source.X, backdrop.X);
float cg = OverlayValueFunction(backdrop.Y, source.Y); float cg = OverlayValueFunction(source.Y, backdrop.Y);
float cb = OverlayValueFunction(backdrop.Z, source.Z); float cb = OverlayValueFunction(source.Z, backdrop.Z);
return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); return Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0));
} }
/// <summary> /// <summary>
/// Hard light effect /// Helper function for Overlay andHardLight modes
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color element</param>
/// <param name="source">Source color</param> /// <param name="source">Source color element</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <returns>Overlay value</returns>
/// <returns>Output color</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)] private static float OverlayValueFunction(float backdrop, float source)
public static Vector4 HardLight(Vector4 backdrop, Vector4 source, float opacity) {
{ return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop));
source.W *= opacity; }
float cr = OverlayValueFunction(source.X, backdrop.X);
float cg = OverlayValueFunction(source.Y, backdrop.Y); /// <summary>
float cb = OverlayValueFunction(source.Z, backdrop.Z); /// General composition function for all modes, with a general solution for alpha channel
/// </summary>
return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); /// <param name="backdrop">Original Backdrop color</param>
} /// <param name="source">Original source color</param>
/// <param name="xform">Desired transformed color, without taking Alpha channel in account</param>
/// <summary> /// <returns>The final color</returns>
/// Helper function for Overlay andHardLight modes /// <remarks>
/// </summary> /// This is the default compositor for "normal" alpha blending, which matches the generated SrcOver compositor.
/// <param name="backdrop">Backdrop color element</param> /// </remarks>
/// <param name="source">Source color element</param> [MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <returns>Overlay value</returns> private static Vector4 Compose(Vector4 backdrop, Vector4 source, Vector4 xform)
[MethodImpl(MethodImplOptions.AggressiveInlining)] {
private static float OverlayValueFunction(float backdrop, float source) // calculate weights
{ float xw = backdrop.W * source.W;
return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); float bw = backdrop.W - xw;
} float sw = source.W - xw;
/// <summary> // calculate final alpha
/// General composition function for all modes, with a general solution for alpha channel float a = xw + bw + sw;
/// </summary>
/// <param name="backdrop">Original Backdrop color</param> // calculate final value
/// <param name="source">Original source color</param> xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon);
/// <param name="xform">Desired transformed color, without taking Alpha channel in account</param> xform.W = a;
/// <returns>The final color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] return xform;
private static Vector4 Compose(Vector4 backdrop, Vector4 source, Vector4 xform) }
{ }
// calculate weights
float xw = backdrop.W * source.W;
float bw = backdrop.W - xw;
float sw = source.W - xw;
// calculate final alpha
float a = xw + bw + sw;
// calculate final value
xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon);
xform.W = a;
return xform;
}
}
} }

42
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs

@ -20,30 +20,30 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
switch (mode) switch (mode)
{ {
case PixelBlenderMode.Multiply: return DefaultPixelBlenders<TPixel>.Multiply.Instance; case PixelBlenderMode.Multiply: return DefaultPixelBlenders<TPixel>.Multiply_SrcOver.Instance;
case PixelBlenderMode.Add: return DefaultPixelBlenders<TPixel>.Add.Instance; case PixelBlenderMode.Add: return DefaultPixelBlenders<TPixel>.Add_SrcOver.Instance;
case PixelBlenderMode.Subtract: return DefaultPixelBlenders<TPixel>.Subtract.Instance; case PixelBlenderMode.Subtract: return DefaultPixelBlenders<TPixel>.Subtract_SrcOver.Instance;
case PixelBlenderMode.Screen: return DefaultPixelBlenders<TPixel>.Screen.Instance; case PixelBlenderMode.Screen: return DefaultPixelBlenders<TPixel>.Screen_SrcOver.Instance;
case PixelBlenderMode.Darken: return DefaultPixelBlenders<TPixel>.Darken.Instance; case PixelBlenderMode.Darken: return DefaultPixelBlenders<TPixel>.Darken_SrcOver.Instance;
case PixelBlenderMode.Lighten: return DefaultPixelBlenders<TPixel>.Lighten.Instance; case PixelBlenderMode.Lighten: return DefaultPixelBlenders<TPixel>.Lighten_SrcOver.Instance;
case PixelBlenderMode.Overlay: return DefaultPixelBlenders<TPixel>.Overlay.Instance; case PixelBlenderMode.Overlay: return DefaultPixelBlenders<TPixel>.Overlay_SrcOver.Instance;
case PixelBlenderMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLight.Instance; case PixelBlenderMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLight_SrcOver.Instance;
case PixelBlenderMode.Src: return DefaultPixelBlenders<TPixel>.Src.Instance; case PixelBlenderMode.Src: return DefaultPixelBlenders<TPixel>.Normal_Src.Instance;
case PixelBlenderMode.Atop: return DefaultPixelBlenders<TPixel>.Atop.Instance; case PixelBlenderMode.Atop: return DefaultPixelBlenders<TPixel>.Normal_SrcAtop.Instance;
case PixelBlenderMode.Over: return DefaultPixelBlenders<TPixel>.Over.Instance; case PixelBlenderMode.Over: return DefaultPixelBlenders<TPixel>.Normal_SrcOver.Instance;
case PixelBlenderMode.In: return DefaultPixelBlenders<TPixel>.In.Instance; case PixelBlenderMode.In: return DefaultPixelBlenders<TPixel>.Normal_SrcIn.Instance;
case PixelBlenderMode.Out: return DefaultPixelBlenders<TPixel>.Out.Instance; case PixelBlenderMode.Out: return DefaultPixelBlenders<TPixel>.Normal_SrcOut.Instance;
case PixelBlenderMode.Dest: return DefaultPixelBlenders<TPixel>.Dest.Instance; case PixelBlenderMode.Dest: return DefaultPixelBlenders<TPixel>.Normal_Dest.Instance;
case PixelBlenderMode.DestAtop: return DefaultPixelBlenders<TPixel>.DestAtop.Instance; case PixelBlenderMode.DestAtop: return DefaultPixelBlenders<TPixel>.Normal_DestAtop.Instance;
case PixelBlenderMode.DestOver: return DefaultPixelBlenders<TPixel>.DestOver.Instance; case PixelBlenderMode.DestOver: return DefaultPixelBlenders<TPixel>.Normal_DestOver.Instance;
case PixelBlenderMode.DestIn: return DefaultPixelBlenders<TPixel>.DestIn.Instance; case PixelBlenderMode.DestIn: return DefaultPixelBlenders<TPixel>.Normal_DestIn.Instance;
case PixelBlenderMode.DestOut: return DefaultPixelBlenders<TPixel>.DestOut.Instance; case PixelBlenderMode.DestOut: return DefaultPixelBlenders<TPixel>.Normal_DestOut.Instance;
case PixelBlenderMode.Clear: return DefaultPixelBlenders<TPixel>.Clear.Instance; case PixelBlenderMode.Clear: return DefaultPixelBlenders<TPixel>.Normal_Clear.Instance;
case PixelBlenderMode.Xor: return DefaultPixelBlenders<TPixel>.Xor.Instance; case PixelBlenderMode.Xor: return DefaultPixelBlenders<TPixel>.Normal_Xor.Instance;
case PixelBlenderMode.Normal: case PixelBlenderMode.Normal:
default: default:
return DefaultPixelBlenders<TPixel>.Normal.Instance; return DefaultPixelBlenders<TPixel>.Normal_SrcOver.Instance;
} }
} }
} }

4
tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs

@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Benchmarks
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
destinationSpan[i] = PorterDuffFunctions.Normal(backgroundSpan[i], sourceSpan[i], amount[i]); destinationSpan[i] = PorterDuffFunctions.Normal_SrcOver(backgroundSpan[i], sourceSpan[i], amount[i]);
} }
PixelOperations<TPixel>.Instance.PackFromVector4(destinationSpan, destination, destination.Length); PixelOperations<TPixel>.Instance.PackFromVector4(destinationSpan, destination, destination.Length);
@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Benchmarks
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
destination[i] = PorterDuffFunctions.Normal(destination[i], source[i], amount[i]); destination[i] = PorterDuffFunctions.Normal_SrcOver(destination[i], source[i], amount[i]);
} }
} }

18
tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(NormalBlendFunctionData))] [MemberData(nameof(NormalBlendFunctionData))]
public void NormalBlendFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void NormalBlendFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Normal((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.Normal_SrcOver((Vector4)back, source, amount);
Assert.Equal(expected, actual); Assert.Equal(expected, actual);
} }
@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(MultiplyFunctionData))] [MemberData(nameof(MultiplyFunctionData))]
public void MultiplyFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void MultiplyFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Multiply((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.Multiply_SrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(AddFunctionData))] [MemberData(nameof(AddFunctionData))]
public void AddFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void AddFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Multiply((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.Multiply_SrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(SubstractFunctionData))] [MemberData(nameof(SubstractFunctionData))]
public void SubstractFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void SubstractFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Subtract((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.Subtract_SrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(ScreenFunctionData))] [MemberData(nameof(ScreenFunctionData))]
public void ScreenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void ScreenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Screen((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.Screen_SrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -117,7 +117,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(DarkenFunctionData))] [MemberData(nameof(DarkenFunctionData))]
public void DarkenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void DarkenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Darken((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.Darken_SrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -136,7 +136,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(LightenFunctionData))] [MemberData(nameof(LightenFunctionData))]
public void LightenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void LightenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Lighten((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.Lighten_SrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(OverlayFunctionData))] [MemberData(nameof(OverlayFunctionData))]
public void OverlayFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void OverlayFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.Overlay((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.Overlay_SrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
@ -174,7 +174,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
[MemberData(nameof(HardLightFunctionData))] [MemberData(nameof(HardLightFunctionData))]
public void HardLightFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) public void HardLightFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
{ {
Vector4 actual = PorterDuffFunctions.HardLight((Vector4)back, source, amount); Vector4 actual = PorterDuffFunctions.HardLight_SrcOver((Vector4)back, source, amount);
VectorAssert.Equal(expected, actual, 5); VectorAssert.Equal(expected, actual, 5);
} }
} }

54
tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void NormalBlendFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void NormalBlendFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Normal((TPixel)(TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.Normal_SrcOver((TPixel)(TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void NormalBlendFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void NormalBlendFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Normal().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.Normal_SrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); Span<TPixel> dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Normal().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.Normal_SrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void MultiplyFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void MultiplyFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Multiply((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.Multiply_SrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void MultiplyFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void MultiplyFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Multiply().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.Multiply_SrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); Span<TPixel> dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Multiply().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.Multiply_SrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void AddFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void AddFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Add((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.Add_SrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void AddFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void AddFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Add().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.Add_SrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -131,7 +131,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); Span<TPixel> dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Add().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.Add_SrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void SubstractFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void SubstractFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Subtract((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.Subtract_SrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -160,7 +160,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void SubstractFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void SubstractFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Subtract().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.Subtract_SrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -170,7 +170,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); Span<TPixel> dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Subtract().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.Subtract_SrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -190,7 +190,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void ScreenFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void ScreenFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Screen((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.Screen_SrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -199,7 +199,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void ScreenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void ScreenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Screen().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.Screen_SrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -209,7 +209,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); Span<TPixel> dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Screen().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.Screen_SrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -229,7 +229,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void DarkenFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void DarkenFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Darken((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.Darken_SrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void DarkenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void DarkenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Darken().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.Darken_SrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -248,7 +248,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); Span<TPixel> dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Darken().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.Darken_SrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -268,7 +268,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void LightenFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void LightenFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Lighten((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.Lighten_SrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -277,7 +277,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void LightenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void LightenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Lighten().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.Lighten_SrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -287,7 +287,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); Span<TPixel> dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Lighten().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.Lighten_SrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -307,7 +307,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void OverlayFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void OverlayFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.Overlay((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.Overlay_SrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -316,7 +316,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void OverlayFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void OverlayFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.Overlay().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.Overlay_SrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -326,7 +326,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); Span<TPixel> dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.Overlay().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.Overlay_SrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -346,7 +346,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void HardLightFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void HardLightFunction<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = PorterDuffFunctions.HardLight((TPixel)back, source, amount); TPixel actual = PorterDuffFunctions.HardLight_SrcOver((TPixel)back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -355,7 +355,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
public void HardLightFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void HardLightFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultPixelBlenders<TPixel>.HardLight().Blend(back, source, amount); TPixel actual = new DefaultPixelBlenders<TPixel>.HardLight_SrcOver().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -365,7 +365,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> dest = new Span<TPixel>(new TPixel[1]); Span<TPixel> dest = new Span<TPixel>(new TPixel[1]);
new DefaultPixelBlenders<TPixel>.HardLight().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultPixelBlenders<TPixel>.HardLight_SrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
} }

84
tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs

@ -17,50 +17,50 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
public static TheoryData<object, Type, PixelBlenderMode> BlenderMappings = new TheoryData<object, Type, PixelBlenderMode>() public static TheoryData<object, Type, PixelBlenderMode> BlenderMappings = new TheoryData<object, Type, PixelBlenderMode>()
{ {
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal), PixelBlenderMode.Normal }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal_SrcOver), PixelBlenderMode.Normal },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Screen), PixelBlenderMode.Screen }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Screen_SrcOver), PixelBlenderMode.Screen },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.HardLight), PixelBlenderMode.HardLight }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.HardLight_SrcOver), PixelBlenderMode.HardLight },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Overlay), PixelBlenderMode.Overlay }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Overlay_SrcOver), PixelBlenderMode.Overlay },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Darken), PixelBlenderMode.Darken }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Darken_SrcOver), PixelBlenderMode.Darken },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Lighten), PixelBlenderMode.Lighten }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Lighten_SrcOver), PixelBlenderMode.Lighten },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Add), PixelBlenderMode.Add }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Add_SrcOver), PixelBlenderMode.Add },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Subtract), PixelBlenderMode.Subtract }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Subtract_SrcOver), PixelBlenderMode.Subtract },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Multiply), PixelBlenderMode.Multiply }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Multiply_SrcOver), PixelBlenderMode.Multiply },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Src), PixelBlenderMode.Src }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal_Src), PixelBlenderMode.Src },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Atop), PixelBlenderMode.Atop }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal_SrcAtop), PixelBlenderMode.Atop },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Over), PixelBlenderMode.Over }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal_SrcOver), PixelBlenderMode.Over },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.In), PixelBlenderMode.In }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal_SrcIn), PixelBlenderMode.In },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Out), PixelBlenderMode.Out }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal_SrcOut), PixelBlenderMode.Out },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Dest), PixelBlenderMode.Dest }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal_Dest), PixelBlenderMode.Dest },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.DestAtop), PixelBlenderMode.DestAtop }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal_DestAtop), PixelBlenderMode.DestAtop },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.DestOver), PixelBlenderMode.DestOver }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal_DestOver), PixelBlenderMode.DestOver },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.DestIn), PixelBlenderMode.DestIn }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal_DestIn), PixelBlenderMode.DestIn },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.DestOut), PixelBlenderMode.DestOut }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal_DestOut), PixelBlenderMode.DestOut },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Clear), PixelBlenderMode.Clear }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal_Clear), PixelBlenderMode.Clear },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Xor), PixelBlenderMode.Xor }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.Normal_Xor), PixelBlenderMode.Xor },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal), PixelBlenderMode.Normal }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal_SrcOver), PixelBlenderMode.Normal },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Screen), PixelBlenderMode.Screen }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Screen_SrcOver), PixelBlenderMode.Screen },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.HardLight), PixelBlenderMode.HardLight }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.HardLight_SrcOver), PixelBlenderMode.HardLight },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Overlay), PixelBlenderMode.Overlay }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Overlay_SrcOver), PixelBlenderMode.Overlay },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Darken), PixelBlenderMode.Darken }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Darken_SrcOver), PixelBlenderMode.Darken },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Lighten), PixelBlenderMode.Lighten }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Lighten_SrcOver), PixelBlenderMode.Lighten },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Add), PixelBlenderMode.Add }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Add_SrcOver), PixelBlenderMode.Add },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Subtract), PixelBlenderMode.Subtract }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Subtract_SrcOver), PixelBlenderMode.Subtract },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Multiply), PixelBlenderMode.Multiply }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Multiply_SrcOver), PixelBlenderMode.Multiply },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Src), PixelBlenderMode.Src }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal_Src), PixelBlenderMode.Src },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Atop), PixelBlenderMode.Atop }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal_SrcAtop), PixelBlenderMode.Atop },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Over), PixelBlenderMode.Over }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal_SrcOver), PixelBlenderMode.Over },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.In), PixelBlenderMode.In }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal_SrcIn), PixelBlenderMode.In },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Out), PixelBlenderMode.Out }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal_SrcOut), PixelBlenderMode.Out },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Dest), PixelBlenderMode.Dest }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal_Dest), PixelBlenderMode.Dest },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.DestAtop), PixelBlenderMode.DestAtop }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal_DestAtop), PixelBlenderMode.DestAtop },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.DestOver), PixelBlenderMode.DestOver }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal_DestOver), PixelBlenderMode.DestOver },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.DestIn), PixelBlenderMode.DestIn }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal_DestIn), PixelBlenderMode.DestIn },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.DestOut), PixelBlenderMode.DestOut }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal_DestOut), PixelBlenderMode.DestOut },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Clear), PixelBlenderMode.Clear }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal_Clear), PixelBlenderMode.Clear },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Xor), PixelBlenderMode.Xor }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.Normal_Xor), PixelBlenderMode.Xor },
}; };

Loading…
Cancel
Save