Browse Source

Add plus and extended pixel blend modes

Introduce the `Plus` alpha composition mode and add support for ColorDodge, ColorBurn, SoftLight, Difference, Exclusion, Hue, Saturation, Color, and Luminosity across straight-alpha and associated-alpha pixel blenders. The generated blender/function tables and selector logic were updated to expose the new combinations, and tests were expanded to cover mode mapping, expected blend results, and parity between straight and premultiplied alpha paths.
pull/3163/head
James Jackson-South 1 week ago
parent
commit
c9af728104
  1. 7
      src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs
  2. 4348
      src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs
  3. 10
      src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt
  4. 129
      src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.cs
  5. 7668
      src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.cs
  6. 14
      src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.tt
  7. 449
      src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.cs
  8. 4348
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs
  9. 10
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt
  10. 7151
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
  11. 57
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt
  12. 404
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs
  13. 45
      src/ImageSharp/PixelFormats/PixelColorBlendingMode.cs
  14. 132
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs
  15. 221
      tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs
  16. 135
      tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs

7
src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs

@ -66,5 +66,10 @@ public enum PixelAlphaCompositionMode
/// <summary>
/// Clear where they overlap.
/// </summary>
Xor
Xor,
/// <summary>
/// Adds the source and destination, clamping the result to the supported color and alpha range.
/// </summary>
Plus,
}

4348
src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs

File diff suppressed because it is too large

10
src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt

@ -29,6 +29,7 @@ var composers = new []
"DestOut",
"Clear",
"Xor",
"Plus",
};
var blenders = new []
@ -42,6 +43,15 @@ var blenders = new []
"Lighten",
"Overlay",
"HardLight",
"ColorDodge",
"ColorBurn",
"SoftLight",
"Difference",
"Exclusion",
"Hue",
"Saturation",
"Color",
"Luminosity",
};
#>
/// <summary>

129
src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.cs

@ -29,6 +29,15 @@ internal static partial class AssociatedAlphaPixelBlenders<TPixel>
PixelColorBlendingMode.Lighten => LightenSrc,
PixelColorBlendingMode.Overlay => OverlaySrc,
PixelColorBlendingMode.HardLight => HardLightSrc,
PixelColorBlendingMode.ColorDodge => ColorDodgeSrc,
PixelColorBlendingMode.ColorBurn => ColorBurnSrc,
PixelColorBlendingMode.SoftLight => SoftLightSrc,
PixelColorBlendingMode.Difference => DifferenceSrc,
PixelColorBlendingMode.Exclusion => ExclusionSrc,
PixelColorBlendingMode.Hue => HueSrc,
PixelColorBlendingMode.Saturation => SaturationSrc,
PixelColorBlendingMode.Color => ColorSrc,
PixelColorBlendingMode.Luminosity => LuminositySrc,
_ => NormalSrc,
},
PixelAlphaCompositionMode.SrcAtop => colorMode switch
@ -41,6 +50,15 @@ internal static partial class AssociatedAlphaPixelBlenders<TPixel>
PixelColorBlendingMode.Lighten => LightenSrcAtop,
PixelColorBlendingMode.Overlay => OverlaySrcAtop,
PixelColorBlendingMode.HardLight => HardLightSrcAtop,
PixelColorBlendingMode.ColorDodge => ColorDodgeSrcAtop,
PixelColorBlendingMode.ColorBurn => ColorBurnSrcAtop,
PixelColorBlendingMode.SoftLight => SoftLightSrcAtop,
PixelColorBlendingMode.Difference => DifferenceSrcAtop,
PixelColorBlendingMode.Exclusion => ExclusionSrcAtop,
PixelColorBlendingMode.Hue => HueSrcAtop,
PixelColorBlendingMode.Saturation => SaturationSrcAtop,
PixelColorBlendingMode.Color => ColorSrcAtop,
PixelColorBlendingMode.Luminosity => LuminositySrcAtop,
_ => NormalSrcAtop,
},
PixelAlphaCompositionMode.SrcIn => colorMode switch
@ -53,6 +71,15 @@ internal static partial class AssociatedAlphaPixelBlenders<TPixel>
PixelColorBlendingMode.Lighten => LightenSrcIn,
PixelColorBlendingMode.Overlay => OverlaySrcIn,
PixelColorBlendingMode.HardLight => HardLightSrcIn,
PixelColorBlendingMode.ColorDodge => ColorDodgeSrcIn,
PixelColorBlendingMode.ColorBurn => ColorBurnSrcIn,
PixelColorBlendingMode.SoftLight => SoftLightSrcIn,
PixelColorBlendingMode.Difference => DifferenceSrcIn,
PixelColorBlendingMode.Exclusion => ExclusionSrcIn,
PixelColorBlendingMode.Hue => HueSrcIn,
PixelColorBlendingMode.Saturation => SaturationSrcIn,
PixelColorBlendingMode.Color => ColorSrcIn,
PixelColorBlendingMode.Luminosity => LuminositySrcIn,
_ => NormalSrcIn,
},
PixelAlphaCompositionMode.SrcOut => colorMode switch
@ -65,6 +92,15 @@ internal static partial class AssociatedAlphaPixelBlenders<TPixel>
PixelColorBlendingMode.Lighten => LightenSrcOut,
PixelColorBlendingMode.Overlay => OverlaySrcOut,
PixelColorBlendingMode.HardLight => HardLightSrcOut,
PixelColorBlendingMode.ColorDodge => ColorDodgeSrcOut,
PixelColorBlendingMode.ColorBurn => ColorBurnSrcOut,
PixelColorBlendingMode.SoftLight => SoftLightSrcOut,
PixelColorBlendingMode.Difference => DifferenceSrcOut,
PixelColorBlendingMode.Exclusion => ExclusionSrcOut,
PixelColorBlendingMode.Hue => HueSrcOut,
PixelColorBlendingMode.Saturation => SaturationSrcOut,
PixelColorBlendingMode.Color => ColorSrcOut,
PixelColorBlendingMode.Luminosity => LuminositySrcOut,
_ => NormalSrcOut,
},
PixelAlphaCompositionMode.Dest => colorMode switch
@ -77,6 +113,15 @@ internal static partial class AssociatedAlphaPixelBlenders<TPixel>
PixelColorBlendingMode.Lighten => LightenDest,
PixelColorBlendingMode.Overlay => OverlayDest,
PixelColorBlendingMode.HardLight => HardLightDest,
PixelColorBlendingMode.ColorDodge => ColorDodgeDest,
PixelColorBlendingMode.ColorBurn => ColorBurnDest,
PixelColorBlendingMode.SoftLight => SoftLightDest,
PixelColorBlendingMode.Difference => DifferenceDest,
PixelColorBlendingMode.Exclusion => ExclusionDest,
PixelColorBlendingMode.Hue => HueDest,
PixelColorBlendingMode.Saturation => SaturationDest,
PixelColorBlendingMode.Color => ColorDest,
PixelColorBlendingMode.Luminosity => LuminosityDest,
_ => NormalDest,
},
PixelAlphaCompositionMode.DestAtop => colorMode switch
@ -89,6 +134,15 @@ internal static partial class AssociatedAlphaPixelBlenders<TPixel>
PixelColorBlendingMode.Lighten => LightenDestAtop,
PixelColorBlendingMode.Overlay => OverlayDestAtop,
PixelColorBlendingMode.HardLight => HardLightDestAtop,
PixelColorBlendingMode.ColorDodge => ColorDodgeDestAtop,
PixelColorBlendingMode.ColorBurn => ColorBurnDestAtop,
PixelColorBlendingMode.SoftLight => SoftLightDestAtop,
PixelColorBlendingMode.Difference => DifferenceDestAtop,
PixelColorBlendingMode.Exclusion => ExclusionDestAtop,
PixelColorBlendingMode.Hue => HueDestAtop,
PixelColorBlendingMode.Saturation => SaturationDestAtop,
PixelColorBlendingMode.Color => ColorDestAtop,
PixelColorBlendingMode.Luminosity => LuminosityDestAtop,
_ => NormalDestAtop,
},
PixelAlphaCompositionMode.DestOver => colorMode switch
@ -101,6 +155,15 @@ internal static partial class AssociatedAlphaPixelBlenders<TPixel>
PixelColorBlendingMode.Lighten => LightenDestOver,
PixelColorBlendingMode.Overlay => OverlayDestOver,
PixelColorBlendingMode.HardLight => HardLightDestOver,
PixelColorBlendingMode.ColorDodge => ColorDodgeDestOver,
PixelColorBlendingMode.ColorBurn => ColorBurnDestOver,
PixelColorBlendingMode.SoftLight => SoftLightDestOver,
PixelColorBlendingMode.Difference => DifferenceDestOver,
PixelColorBlendingMode.Exclusion => ExclusionDestOver,
PixelColorBlendingMode.Hue => HueDestOver,
PixelColorBlendingMode.Saturation => SaturationDestOver,
PixelColorBlendingMode.Color => ColorDestOver,
PixelColorBlendingMode.Luminosity => LuminosityDestOver,
_ => NormalDestOver,
},
PixelAlphaCompositionMode.DestIn => colorMode switch
@ -113,6 +176,15 @@ internal static partial class AssociatedAlphaPixelBlenders<TPixel>
PixelColorBlendingMode.Lighten => LightenDestIn,
PixelColorBlendingMode.Overlay => OverlayDestIn,
PixelColorBlendingMode.HardLight => HardLightDestIn,
PixelColorBlendingMode.ColorDodge => ColorDodgeDestIn,
PixelColorBlendingMode.ColorBurn => ColorBurnDestIn,
PixelColorBlendingMode.SoftLight => SoftLightDestIn,
PixelColorBlendingMode.Difference => DifferenceDestIn,
PixelColorBlendingMode.Exclusion => ExclusionDestIn,
PixelColorBlendingMode.Hue => HueDestIn,
PixelColorBlendingMode.Saturation => SaturationDestIn,
PixelColorBlendingMode.Color => ColorDestIn,
PixelColorBlendingMode.Luminosity => LuminosityDestIn,
_ => NormalDestIn,
},
PixelAlphaCompositionMode.DestOut => colorMode switch
@ -125,6 +197,15 @@ internal static partial class AssociatedAlphaPixelBlenders<TPixel>
PixelColorBlendingMode.Lighten => LightenDestOut,
PixelColorBlendingMode.Overlay => OverlayDestOut,
PixelColorBlendingMode.HardLight => HardLightDestOut,
PixelColorBlendingMode.ColorDodge => ColorDodgeDestOut,
PixelColorBlendingMode.ColorBurn => ColorBurnDestOut,
PixelColorBlendingMode.SoftLight => SoftLightDestOut,
PixelColorBlendingMode.Difference => DifferenceDestOut,
PixelColorBlendingMode.Exclusion => ExclusionDestOut,
PixelColorBlendingMode.Hue => HueDestOut,
PixelColorBlendingMode.Saturation => SaturationDestOut,
PixelColorBlendingMode.Color => ColorDestOut,
PixelColorBlendingMode.Luminosity => LuminosityDestOut,
_ => NormalDestOut,
},
PixelAlphaCompositionMode.Clear => colorMode switch
@ -137,6 +218,15 @@ internal static partial class AssociatedAlphaPixelBlenders<TPixel>
PixelColorBlendingMode.Lighten => LightenClear,
PixelColorBlendingMode.Overlay => OverlayClear,
PixelColorBlendingMode.HardLight => HardLightClear,
PixelColorBlendingMode.ColorDodge => ColorDodgeClear,
PixelColorBlendingMode.ColorBurn => ColorBurnClear,
PixelColorBlendingMode.SoftLight => SoftLightClear,
PixelColorBlendingMode.Difference => DifferenceClear,
PixelColorBlendingMode.Exclusion => ExclusionClear,
PixelColorBlendingMode.Hue => HueClear,
PixelColorBlendingMode.Saturation => SaturationClear,
PixelColorBlendingMode.Color => ColorClear,
PixelColorBlendingMode.Luminosity => LuminosityClear,
_ => NormalClear,
},
PixelAlphaCompositionMode.Xor => colorMode switch
@ -149,8 +239,38 @@ internal static partial class AssociatedAlphaPixelBlenders<TPixel>
PixelColorBlendingMode.Lighten => LightenXor,
PixelColorBlendingMode.Overlay => OverlayXor,
PixelColorBlendingMode.HardLight => HardLightXor,
PixelColorBlendingMode.ColorDodge => ColorDodgeXor,
PixelColorBlendingMode.ColorBurn => ColorBurnXor,
PixelColorBlendingMode.SoftLight => SoftLightXor,
PixelColorBlendingMode.Difference => DifferenceXor,
PixelColorBlendingMode.Exclusion => ExclusionXor,
PixelColorBlendingMode.Hue => HueXor,
PixelColorBlendingMode.Saturation => SaturationXor,
PixelColorBlendingMode.Color => ColorXor,
PixelColorBlendingMode.Luminosity => LuminosityXor,
_ => NormalXor,
},
PixelAlphaCompositionMode.Plus => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplyPlus,
PixelColorBlendingMode.Add => AddPlus,
PixelColorBlendingMode.Subtract => SubtractPlus,
PixelColorBlendingMode.Screen => ScreenPlus,
PixelColorBlendingMode.Darken => DarkenPlus,
PixelColorBlendingMode.Lighten => LightenPlus,
PixelColorBlendingMode.Overlay => OverlayPlus,
PixelColorBlendingMode.HardLight => HardLightPlus,
PixelColorBlendingMode.ColorDodge => ColorDodgePlus,
PixelColorBlendingMode.ColorBurn => ColorBurnPlus,
PixelColorBlendingMode.SoftLight => SoftLightPlus,
PixelColorBlendingMode.Difference => DifferencePlus,
PixelColorBlendingMode.Exclusion => ExclusionPlus,
PixelColorBlendingMode.Hue => HuePlus,
PixelColorBlendingMode.Saturation => SaturationPlus,
PixelColorBlendingMode.Color => ColorPlus,
PixelColorBlendingMode.Luminosity => LuminosityPlus,
_ => NormalPlus,
},
_ => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplySrcOver,
@ -161,6 +281,15 @@ internal static partial class AssociatedAlphaPixelBlenders<TPixel>
PixelColorBlendingMode.Lighten => LightenSrcOver,
PixelColorBlendingMode.Overlay => OverlaySrcOver,
PixelColorBlendingMode.HardLight => HardLightSrcOver,
PixelColorBlendingMode.ColorDodge => ColorDodgeSrcOver,
PixelColorBlendingMode.ColorBurn => ColorBurnSrcOver,
PixelColorBlendingMode.SoftLight => SoftLightSrcOver,
PixelColorBlendingMode.Difference => DifferenceSrcOver,
PixelColorBlendingMode.Exclusion => ExclusionSrcOver,
PixelColorBlendingMode.Hue => HueSrcOver,
PixelColorBlendingMode.Saturation => SaturationSrcOver,
PixelColorBlendingMode.Color => ColorSrcOver,
PixelColorBlendingMode.Luminosity => LuminositySrcOver,
_ => NormalSrcOver,
},
};

7668
src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.cs

File diff suppressed because it is too large

14
src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.tt

@ -73,6 +73,7 @@ internal static partial class AssociatedAlphaPorterDuffFunctions
"DestOut",
"Clear",
"Xor",
"Plus",
};
private static readonly string[] Blenders =
@ -86,6 +87,15 @@ internal static partial class AssociatedAlphaPorterDuffFunctions
"Lighten",
"Overlay",
"HardLight",
"ColorDodge",
"ColorBurn",
"SoftLight",
"Difference",
"Exclusion",
"Hue",
"Saturation",
"Color",
"Luminosity",
};
private static readonly string[] VectorTypes =
@ -131,6 +141,10 @@ internal static partial class AssociatedAlphaPorterDuffFunctions
return "Out(source, backdrop)";
case "Clear":
return zero;
case "Plus":
return normal
? "PlusNormal(backdrop, source)"
: $"Plus(backdrop, source, {blender}(backdrop, source))";
default:
return "Xor(backdrop, source)";
}

449
src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.cs

@ -280,6 +280,384 @@ internal static partial class AssociatedAlphaPorterDuffFunctions
return Vector512.ConditionalSelect(useRight, right, left);
}
/// <summary>
/// Calculates the associated overlap term for ColorDodge blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 ColorDodge(Vector4 backdrop, Vector4 source)
{
Vector4 backdropAlpha = Numerics.PermuteW(backdrop);
Vector4 sourceAlpha = Numerics.PermuteW(source);
Vector4 unassociatedBackdrop = backdrop;
Vector4 unassociatedSource = source;
// This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha.
Numerics.UnPremultiply(ref unassociatedBackdrop);
Numerics.UnPremultiply(ref unassociatedSource);
return PorterDuffFunctions.ColorDodge(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="ColorDodge(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> ColorDodge(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector256<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.ColorDodge(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="ColorDodge(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> ColorDodge(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector512<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.ColorDodge(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <summary>
/// Calculates the associated overlap term for ColorBurn blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 ColorBurn(Vector4 backdrop, Vector4 source)
{
Vector4 backdropAlpha = Numerics.PermuteW(backdrop);
Vector4 sourceAlpha = Numerics.PermuteW(source);
Vector4 unassociatedBackdrop = backdrop;
Vector4 unassociatedSource = source;
// This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha.
Numerics.UnPremultiply(ref unassociatedBackdrop);
Numerics.UnPremultiply(ref unassociatedSource);
return PorterDuffFunctions.ColorBurn(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="ColorBurn(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> ColorBurn(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector256<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.ColorBurn(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="ColorBurn(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> ColorBurn(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector512<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.ColorBurn(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <summary>
/// Calculates the associated overlap term for SoftLight blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 SoftLight(Vector4 backdrop, Vector4 source)
{
Vector4 backdropAlpha = Numerics.PermuteW(backdrop);
Vector4 sourceAlpha = Numerics.PermuteW(source);
Vector4 unassociatedBackdrop = backdrop;
Vector4 unassociatedSource = source;
// This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha.
Numerics.UnPremultiply(ref unassociatedBackdrop);
Numerics.UnPremultiply(ref unassociatedSource);
return PorterDuffFunctions.SoftLight(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="SoftLight(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> SoftLight(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector256<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.SoftLight(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="SoftLight(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> SoftLight(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector512<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.SoftLight(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <summary>
/// Calculates the associated overlap term for Difference blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Difference(Vector4 backdrop, Vector4 source)
{
Vector4 backdropAlpha = Numerics.PermuteW(backdrop);
Vector4 sourceAlpha = Numerics.PermuteW(source);
Vector4 unassociatedBackdrop = backdrop;
Vector4 unassociatedSource = source;
// This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha.
Numerics.UnPremultiply(ref unassociatedBackdrop);
Numerics.UnPremultiply(ref unassociatedSource);
return PorterDuffFunctions.Difference(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="Difference(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Difference(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector256<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.Difference(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="Difference(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Difference(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector512<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.Difference(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <summary>
/// Calculates the associated overlap term for Exclusion blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Exclusion(Vector4 backdrop, Vector4 source)
{
Vector4 backdropAlpha = Numerics.PermuteW(backdrop);
Vector4 sourceAlpha = Numerics.PermuteW(source);
Vector4 unassociatedBackdrop = backdrop;
Vector4 unassociatedSource = source;
// This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha.
Numerics.UnPremultiply(ref unassociatedBackdrop);
Numerics.UnPremultiply(ref unassociatedSource);
return PorterDuffFunctions.Exclusion(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="Exclusion(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Exclusion(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector256<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.Exclusion(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="Exclusion(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Exclusion(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector512<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.Exclusion(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <summary>
/// Calculates the associated overlap term for Hue blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Hue(Vector4 backdrop, Vector4 source)
{
Vector4 backdropAlpha = Numerics.PermuteW(backdrop);
Vector4 sourceAlpha = Numerics.PermuteW(source);
Vector4 unassociatedBackdrop = backdrop;
Vector4 unassociatedSource = source;
// This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha.
Numerics.UnPremultiply(ref unassociatedBackdrop);
Numerics.UnPremultiply(ref unassociatedSource);
return PorterDuffFunctions.Hue(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="Hue(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Hue(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector256<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.Hue(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="Hue(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Hue(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector512<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.Hue(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <summary>
/// Calculates the associated overlap term for Saturation blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Saturation(Vector4 backdrop, Vector4 source)
{
Vector4 backdropAlpha = Numerics.PermuteW(backdrop);
Vector4 sourceAlpha = Numerics.PermuteW(source);
Vector4 unassociatedBackdrop = backdrop;
Vector4 unassociatedSource = source;
// This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha.
Numerics.UnPremultiply(ref unassociatedBackdrop);
Numerics.UnPremultiply(ref unassociatedSource);
return PorterDuffFunctions.Saturation(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="Saturation(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Saturation(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector256<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.Saturation(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="Saturation(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Saturation(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector512<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.Saturation(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <summary>
/// Calculates the associated overlap term for Color blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Color(Vector4 backdrop, Vector4 source)
{
Vector4 backdropAlpha = Numerics.PermuteW(backdrop);
Vector4 sourceAlpha = Numerics.PermuteW(source);
Vector4 unassociatedBackdrop = backdrop;
Vector4 unassociatedSource = source;
// This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha.
Numerics.UnPremultiply(ref unassociatedBackdrop);
Numerics.UnPremultiply(ref unassociatedSource);
return PorterDuffFunctions.Color(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="Color(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Color(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector256<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.Color(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="Color(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Color(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector512<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.Color(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <summary>
/// Calculates the associated overlap term for Luminosity blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Luminosity(Vector4 backdrop, Vector4 source)
{
Vector4 backdropAlpha = Numerics.PermuteW(backdrop);
Vector4 sourceAlpha = Numerics.PermuteW(source);
Vector4 unassociatedBackdrop = backdrop;
Vector4 unassociatedSource = source;
// This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha.
Numerics.UnPremultiply(ref unassociatedBackdrop);
Numerics.UnPremultiply(ref unassociatedSource);
return PorterDuffFunctions.Luminosity(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="Luminosity(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Luminosity(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector256<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.Luminosity(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <inheritdoc cref="Luminosity(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Luminosity(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha);
Vector512<float> unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha);
return PorterDuffFunctions.Luminosity(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha;
}
/// <summary>
/// Composites an associated source over an associated destination without a color-blending function.
/// </summary>
@ -310,6 +688,77 @@ internal static partial class AssociatedAlphaPorterDuffFunctions
return source + (destination * (Vector512.Create(1F) - sourceAlpha));
}
/// <summary>
/// Adds associated source and destination vectors without a color-blending function.
/// </summary>
/// <param name="destination">The associated destination vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The clamped associated composition result.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 PlusNormal(Vector4 destination, Vector4 source)
{
Vector4 alpha = Vector4.Min(Vector4.One, Numerics.PermuteW(source) + Numerics.PermuteW(destination));
return Numerics.WithW(Vector4.Min(Vector4.One, destination + source), alpha);
}
/// <inheritdoc cref="PlusNormal(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> PlusNormal(Vector256<float> destination, Vector256<float> source)
{
Vector256<float> one = Vector256.Create(1F);
Vector256<float> alpha = Vector256.Min(one, Avx.Permute(source, ShuffleAlphaControl) + Avx.Permute(destination, ShuffleAlphaControl));
return Avx.Blend(Vector256.Min(one, destination + source), alpha, BlendAlphaControl);
}
/// <inheritdoc cref="PlusNormal(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> PlusNormal(Vector512<float> destination, Vector512<float> source)
{
Vector512<float> one = Vector512.Create(1F);
Vector512<float> alpha = Vector512.Min(one, Vector512_.ShuffleNative(source, ShuffleAlphaControl) + Vector512_.ShuffleNative(destination, ShuffleAlphaControl));
return Vector512.ConditionalSelect(AlphaMask512(), alpha, Vector512.Min(one, destination + source));
}
/// <summary>
/// Adds associated source and destination vectors using a color-blended overlap term.
/// </summary>
/// <param name="destination">The associated destination vector.</param>
/// <param name="source">The associated source vector.</param>
/// <param name="overlap">The associated overlap term produced by the color-blending function.</param>
/// <returns>The clamped associated composition result.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Plus(Vector4 destination, Vector4 source, Vector4 overlap)
{
Vector4 destinationAlpha = Numerics.PermuteW(destination);
Vector4 alpha = Vector4.Min(Vector4.One, Numerics.PermuteW(source) + destinationAlpha);
// The blended source contributes straight source color outside the destination and the blend result in the overlap.
Vector4 result = destination + (source * (Vector4.One - destinationAlpha)) + overlap;
return Numerics.WithW(Vector4.Min(Vector4.One, result), alpha);
}
/// <inheritdoc cref="Plus(Vector4, Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Plus(Vector256<float> destination, Vector256<float> source, Vector256<float> overlap)
{
Vector256<float> one = Vector256.Create(1F);
Vector256<float> destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl);
Vector256<float> alpha = Vector256.Min(one, Avx.Permute(source, ShuffleAlphaControl) + destinationAlpha);
Vector256<float> result = destination + (source * (one - destinationAlpha)) + overlap;
return Avx.Blend(Vector256.Min(one, result), alpha, BlendAlphaControl);
}
/// <inheritdoc cref="Plus(Vector4, Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Plus(Vector512<float> destination, Vector512<float> source, Vector512<float> overlap)
{
Vector512<float> one = Vector512.Create(1F);
Vector512<float> destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl);
Vector512<float> alpha = Vector512.Min(one, Vector512_.ShuffleNative(source, ShuffleAlphaControl) + destinationAlpha);
Vector512<float> result = destination + (source * (one - destinationAlpha)) + overlap;
return Vector512.ConditionalSelect(AlphaMask512(), alpha, Vector512.Min(one, result));
}
/// <summary>
/// Composites an associated source atop an associated destination without a color-blending function.
/// </summary>

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

File diff suppressed because it is too large

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

@ -29,6 +29,7 @@ var composers = new []
"DestOut",
"Clear",
"Xor",
"Plus",
};
var blenders = new []
@ -42,6 +43,15 @@ var blenders = new []
"Lighten",
"Overlay",
"HardLight",
"ColorDodge",
"ColorBurn",
"SoftLight",
"Difference",
"Exclusion",
"Hue",
"Saturation",
"Color",
"Luminosity",
};
#>
/// <summary>

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

File diff suppressed because it is too large

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

@ -464,6 +464,51 @@ internal static partial class PorterDuffFunctions
public static Vector512<float> <#=blender#>Xor(Vector512<float> backdrop, Vector512<float> source, Vector512<float> opacity)
=> Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512()));
/// <summary>
/// Returns the result of the "<#=blender#>Plus" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 <#=blender#>Plus(Vector4 backdrop, Vector4 source, float opacity)
{
source = Numerics.WithW(source, source * opacity);
return Plus(backdrop, source, <#=blender#>(backdrop, source));
}
/// <summary>
/// Returns the result of the "<#=blender#>Plus" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector256{Single}"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> <#=blender#>Plus(Vector256<float> backdrop, Vector256<float> source, Vector256<float> opacity)
{
source = Avx.Blend(source, source * opacity, BlendAlphaControl);
return Plus(backdrop, source, <#=blender#>(backdrop, source));
}
/// <summary>
/// Returns the result of the "<#=blender#>Plus" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector512{Single}"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> <#=blender#>Plus(Vector512<float> backdrop, Vector512<float> source, Vector512<float> opacity)
{
source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512());
return Plus(backdrop, source, <#=blender#>(backdrop, source));
}
/// <summary>
/// Returns the result of the "<#=blender#>Clear" compositing equation.
/// </summary>
@ -535,6 +580,7 @@ var composers = new []{
"DestOut",
"Clear",
"Xor",
"Plus",
};
var blenders = new []{
@ -546,7 +592,16 @@ var blenders = new []{
"Darken",
"Lighten",
"Overlay",
"HardLight"
"HardLight",
"ColorDodge",
"ColorBurn",
"SoftLight",
"Difference",
"Exclusion",
"Hue",
"Saturation",
"Color",
"Luminosity",
};
foreach(var blender in blenders)

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

@ -324,6 +324,252 @@ internal static partial class PorterDuffFunctions
return Vector512.Min(Vector512.Create(1F), Vector512.ConditionalSelect(AlphaMask512(), Vector512<float>.Zero, color));
}
/// <summary>
/// Returns the result of the "ColorDodge" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>The blended color.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 ColorDodge(Vector4 backdrop, Vector4 source)
=> new(
ColorDodgeValue(backdrop.X, source.X),
ColorDodgeValue(backdrop.Y, source.Y),
ColorDodgeValue(backdrop.Z, source.Z),
0F);
/// <inheritdoc cref="ColorDodge(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> ColorDodge(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> one = Vector256.Create(1F);
Vector256<float> result = Vector256.Min(one, backdrop / (one - source));
// The explicit singular cases avoid the undefined 0 / 0 result while preserving the order required by the specification.
result = Avx.BlendVariable(result, one, Avx.CompareEqual(source, one));
result = Avx.BlendVariable(result, Vector256<float>.Zero, Avx.CompareEqual(backdrop, Vector256<float>.Zero));
return Avx.Blend(result, Vector256<float>.Zero, BlendAlphaControl);
}
/// <inheritdoc cref="ColorDodge(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> ColorDodge(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> one = Vector512.Create(1F);
Vector512<float> result = Vector512.Min(one, backdrop / (one - source));
result = Vector512.ConditionalSelect(Vector512.Equals(source, one), one, result);
result = Vector512.ConditionalSelect(Vector512.Equals(backdrop, Vector512<float>.Zero), Vector512<float>.Zero, result);
return Vector512.ConditionalSelect(AlphaMask512(), Vector512<float>.Zero, result);
}
/// <summary>
/// Returns the result of the "ColorBurn" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>The blended color.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 ColorBurn(Vector4 backdrop, Vector4 source)
=> new(
ColorBurnValue(backdrop.X, source.X),
ColorBurnValue(backdrop.Y, source.Y),
ColorBurnValue(backdrop.Z, source.Z),
0F);
/// <inheritdoc cref="ColorBurn(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> ColorBurn(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> one = Vector256.Create(1F);
Vector256<float> result = one - Vector256.Min(one, (one - backdrop) / source);
// As with ColorDodge, resolve the branch singularities explicitly so transparent lanes never produce NaN.
result = Avx.BlendVariable(result, Vector256<float>.Zero, Avx.CompareEqual(source, Vector256<float>.Zero));
result = Avx.BlendVariable(result, one, Avx.CompareEqual(backdrop, one));
return Avx.Blend(result, Vector256<float>.Zero, BlendAlphaControl);
}
/// <inheritdoc cref="ColorBurn(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> ColorBurn(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> one = Vector512.Create(1F);
Vector512<float> result = one - Vector512.Min(one, (one - backdrop) / source);
result = Vector512.ConditionalSelect(Vector512.Equals(source, Vector512<float>.Zero), Vector512<float>.Zero, result);
result = Vector512.ConditionalSelect(Vector512.Equals(backdrop, one), one, result);
return Vector512.ConditionalSelect(AlphaMask512(), Vector512<float>.Zero, result);
}
/// <summary>
/// Returns the result of the "SoftLight" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>The blended color.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 SoftLight(Vector4 backdrop, Vector4 source)
=> new(
SoftLightValue(backdrop.X, source.X),
SoftLightValue(backdrop.Y, source.Y),
SoftLightValue(backdrop.Z, source.Z),
0F);
/// <inheritdoc cref="SoftLight(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> SoftLight(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> one = Vector256.Create(1F);
Vector256<float> two = Vector256.Create(2F);
Vector256<float> dark = backdrop - ((one - (two * source)) * backdrop * (one - backdrop));
Vector256<float> polynomial = ((((Vector256.Create(16F) * backdrop) - Vector256.Create(12F)) * backdrop) + Vector256.Create(4F)) * backdrop;
Vector256<float> d = Avx.BlendVariable(polynomial, Avx.Sqrt(backdrop), Avx.CompareGreaterThan(backdrop, Vector256.Create(.25F)));
Vector256<float> light = backdrop + (((two * source) - one) * (d - backdrop));
Vector256<float> result = Avx.BlendVariable(dark, light, Avx.CompareGreaterThan(source, Vector256.Create(.5F)));
return Avx.Blend(result, Vector256<float>.Zero, BlendAlphaControl);
}
/// <inheritdoc cref="SoftLight(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> SoftLight(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> one = Vector512.Create(1F);
Vector512<float> two = Vector512.Create(2F);
Vector512<float> dark = backdrop - ((one - (two * source)) * backdrop * (one - backdrop));
Vector512<float> polynomial = ((((Vector512.Create(16F) * backdrop) - Vector512.Create(12F)) * backdrop) + Vector512.Create(4F)) * backdrop;
Vector512<float> d = Vector512.ConditionalSelect(Avx512F.CompareGreaterThan(backdrop, Vector512.Create(.25F)), Vector512.Sqrt(backdrop), polynomial);
Vector512<float> light = backdrop + (((two * source) - one) * (d - backdrop));
Vector512<float> result = Vector512.ConditionalSelect(Avx512F.CompareGreaterThan(source, Vector512.Create(.5F)), light, dark);
return Vector512.ConditionalSelect(AlphaMask512(), Vector512<float>.Zero, result);
}
/// <summary>
/// Returns the result of the "Difference" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>The blended color.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Difference(Vector4 backdrop, Vector4 source)
=> Vector4.Abs(backdrop - source);
/// <inheritdoc cref="Difference(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Difference(Vector256<float> backdrop, Vector256<float> source)
=> Vector256.Abs(backdrop - source);
/// <inheritdoc cref="Difference(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Difference(Vector512<float> backdrop, Vector512<float> source)
=> Vector512.Abs(backdrop - source);
/// <summary>
/// Returns the result of the "Exclusion" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>The blended color.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Exclusion(Vector4 backdrop, Vector4 source)
=> backdrop + source - (2F * backdrop * source);
/// <inheritdoc cref="Exclusion(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Exclusion(Vector256<float> backdrop, Vector256<float> source)
=> backdrop + source - (Vector256.Create(2F) * backdrop * source);
/// <inheritdoc cref="Exclusion(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Exclusion(Vector512<float> backdrop, Vector512<float> source)
=> backdrop + source - (Vector512.Create(2F) * backdrop * source);
/// <summary>
/// Returns the result of the "Hue" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>The blended color.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Hue(Vector4 backdrop, Vector4 source)
=> SetLuminosity(SetSaturation(source, Saturation(backdrop)), Luminosity(backdrop));
/// <inheritdoc cref="Hue(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Hue(Vector256<float> backdrop, Vector256<float> source)
=> Vector256.Create(
Hue(backdrop.GetLower().AsVector4(), source.GetLower().AsVector4()).AsVector128(),
Hue(backdrop.GetUpper().AsVector4(), source.GetUpper().AsVector4()).AsVector128());
/// <inheritdoc cref="Hue(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Hue(Vector512<float> backdrop, Vector512<float> source)
=> Vector512.Create(Hue(backdrop.GetLower(), source.GetLower()), Hue(backdrop.GetUpper(), source.GetUpper()));
/// <summary>
/// Returns the result of the "Saturation" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>The blended color.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Saturation(Vector4 backdrop, Vector4 source)
=> SetLuminosity(SetSaturation(backdrop, Saturation(source)), Luminosity(backdrop));
/// <inheritdoc cref="Saturation(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Saturation(Vector256<float> backdrop, Vector256<float> source)
=> Vector256.Create(
Saturation(backdrop.GetLower().AsVector4(), source.GetLower().AsVector4()).AsVector128(),
Saturation(backdrop.GetUpper().AsVector4(), source.GetUpper().AsVector4()).AsVector128());
/// <inheritdoc cref="Saturation(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Saturation(Vector512<float> backdrop, Vector512<float> source)
=> Vector512.Create(Saturation(backdrop.GetLower(), source.GetLower()), Saturation(backdrop.GetUpper(), source.GetUpper()));
/// <summary>
/// Returns the result of the "Color" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>The blended color.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Color(Vector4 backdrop, Vector4 source)
=> SetLuminosity(source, Luminosity(backdrop));
/// <inheritdoc cref="Color(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Color(Vector256<float> backdrop, Vector256<float> source)
=> Vector256.Create(
Color(backdrop.GetLower().AsVector4(), source.GetLower().AsVector4()).AsVector128(),
Color(backdrop.GetUpper().AsVector4(), source.GetUpper().AsVector4()).AsVector128());
/// <inheritdoc cref="Color(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Color(Vector512<float> backdrop, Vector512<float> source)
=> Vector512.Create(Color(backdrop.GetLower(), source.GetLower()), Color(backdrop.GetUpper(), source.GetUpper()));
/// <summary>
/// Returns the result of the "Luminosity" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>The blended color.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Luminosity(Vector4 backdrop, Vector4 source)
=> SetLuminosity(backdrop, Luminosity(source));
/// <inheritdoc cref="Luminosity(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Luminosity(Vector256<float> backdrop, Vector256<float> source)
=> Vector256.Create(
Luminosity(backdrop.GetLower().AsVector4(), source.GetLower().AsVector4()).AsVector128(),
Luminosity(backdrop.GetUpper().AsVector4(), source.GetUpper().AsVector4()).AsVector128());
/// <inheritdoc cref="Luminosity(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Luminosity(Vector512<float> backdrop, Vector512<float> source)
=> Vector512.Create(Luminosity(backdrop.GetLower(), source.GetLower()), Luminosity(backdrop.GetUpper(), source.GetUpper()));
/// <summary>
/// Applies raster coverage to a Porter-Duff composition result.
/// </summary>
@ -385,6 +631,113 @@ internal static partial class PorterDuffFunctions
return Numerics.UnPremultiply(result, Vector512_.ShuffleNative(result, ShuffleAlphaControl));
}
/// <summary>
/// Calculates one ColorDodge component, including the singular cases defined by the blending model.
/// </summary>
/// <param name="backdrop">The backdrop component.</param>
/// <param name="source">The source component.</param>
/// <returns>The blended component.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float ColorDodgeValue(float backdrop, float source)
=> backdrop == 0F ? 0F : source == 1F ? 1F : MathF.Min(1F, backdrop / (1F - source));
/// <summary>
/// Calculates one ColorBurn component, including the singular cases defined by the blending model.
/// </summary>
/// <param name="backdrop">The backdrop component.</param>
/// <param name="source">The source component.</param>
/// <returns>The blended component.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float ColorBurnValue(float backdrop, float source)
=> backdrop == 1F ? 1F : source == 0F ? 0F : 1F - MathF.Min(1F, (1F - backdrop) / source);
/// <summary>
/// Calculates one SoftLight component.
/// </summary>
/// <param name="backdrop">The backdrop component.</param>
/// <param name="source">The source component.</param>
/// <returns>The blended component.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float SoftLightValue(float backdrop, float source)
{
if (source <= .5F)
{
return backdrop - ((1F - (2F * source)) * backdrop * (1F - backdrop));
}
// The cubic segment joins the square-root segment smoothly while avoiding its steep slope near zero.
float d = backdrop <= .25F
? (((((16F * backdrop) - 12F) * backdrop) + 4F) * backdrop)
: MathF.Sqrt(backdrop);
return backdrop + (((2F * source) - 1F) * (d - backdrop));
}
/// <summary>
/// Calculates the saturation of an RGB color.
/// </summary>
/// <param name="color">The color.</param>
/// <returns>The difference between the maximum and minimum RGB components.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Saturation(Vector4 color)
=> MathF.Max(color.X, MathF.Max(color.Y, color.Z)) - MathF.Min(color.X, MathF.Min(color.Y, color.Z));
/// <summary>
/// Calculates the luminosity of an RGB color.
/// </summary>
/// <param name="color">The color.</param>
/// <returns>The weighted RGB luminosity.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Luminosity(Vector4 color)
=> (.3F * color.X) + (.59F * color.Y) + (.11F * color.Z);
/// <summary>
/// Replaces the saturation of an RGB color while retaining its channel ordering.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="saturation">The required saturation.</param>
/// <returns>The adjusted color.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 SetSaturation(Vector4 color, float saturation)
{
float minimum = MathF.Min(color.X, MathF.Min(color.Y, color.Z));
float range = MathF.Max(color.X, MathF.Max(color.Y, color.Z)) - minimum;
if (range == 0F)
{
return Vector4.Zero;
}
// Translating the minimum to zero and scaling the range is equivalent to the specification's ordered-channel construction.
return (color - new Vector4(minimum)) * (saturation / range);
}
/// <summary>
/// Replaces the luminosity of an RGB color and clips the result to the representable gamut.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="luminosity">The required luminosity.</param>
/// <returns>The adjusted color.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 SetLuminosity(Vector4 color, float luminosity)
{
color += new Vector4(luminosity - Luminosity(color));
float minimum = MathF.Min(color.X, MathF.Min(color.Y, color.Z));
if (minimum < 0F)
{
// Pull every component toward the requested luminosity by the same ratio so hue is preserved.
color = new Vector4(luminosity) + ((color - new Vector4(luminosity)) * (luminosity / (luminosity - minimum)));
}
float maximum = MathF.Max(color.X, MathF.Max(color.Y, color.Z));
if (maximum > 1F)
{
color = new Vector4(luminosity) + ((color - new Vector4(luminosity)) * ((1F - luminosity) / (maximum - luminosity)));
}
return Numerics.WithW(color, Vector4.Zero);
}
/// <summary>
/// Helper function for Overlay and HardLight modes
/// </summary>
@ -783,6 +1136,57 @@ internal static partial class PorterDuffFunctions
return Numerics.UnPremultiply(color, alpha);
}
/// <summary>
/// Returns the result of the "Plus" compositing equation.
/// </summary>
/// <param name="destination">The destination vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="blend">The blended source and destination color.</param>
/// <returns>The composition result.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Plus(Vector4 destination, Vector4 source, Vector4 blend)
{
Vector4 sourceAlpha = Numerics.PermuteW(source);
Vector4 destinationAlpha = Numerics.PermuteW(destination);
Vector4 overlap = sourceAlpha * destinationAlpha;
// Plus retains both non-overlapping regions and uses the selected blend mode in their overlap.
Vector4 color = (destination * destinationAlpha) + (source * (sourceAlpha - overlap)) + (blend * overlap);
Vector4 alpha = Vector4.Min(Vector4.One, sourceAlpha + destinationAlpha);
color = Vector4.Min(Vector4.One, color);
Numerics.UnPremultiply(ref color, alpha);
return color;
}
/// <inheritdoc cref="Plus(Vector4, Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Plus(Vector256<float> destination, Vector256<float> source, Vector256<float> blend)
{
Vector256<float> one = Vector256.Create(1F);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl);
Vector256<float> overlap = sourceAlpha * destinationAlpha;
Vector256<float> color = (destination * destinationAlpha) + (source * (sourceAlpha - overlap)) + (blend * overlap);
Vector256<float> alpha = Vector256.Min(one, sourceAlpha + destinationAlpha);
return Numerics.UnPremultiply(Vector256.Min(one, color), alpha);
}
/// <inheritdoc cref="Plus(Vector4, Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Plus(Vector512<float> destination, Vector512<float> source, Vector512<float> blend)
{
Vector512<float> one = Vector512.Create(1F);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl);
Vector512<float> overlap = sourceAlpha * destinationAlpha;
Vector512<float> color = (destination * destinationAlpha) + (source * (sourceAlpha - overlap)) + (blend * overlap);
Vector512<float> alpha = Vector512.Min(one, sourceAlpha + destinationAlpha);
return Numerics.UnPremultiply(Vector512.Min(one, color), alpha);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 Clear(Vector4 backdrop, Vector4 source) => Vector4.Zero;

45
src/ImageSharp/PixelFormats/PixelColorBlendingMode.cs

@ -52,4 +52,49 @@ public enum PixelColorBlendingMode
/// Multiplies or screens the colors, depending on the source value.
/// </summary>
HardLight,
/// <summary>
/// Brightens the backdrop color to reflect the source color.
/// </summary>
ColorDodge,
/// <summary>
/// Darkens the backdrop color to reflect the source color.
/// </summary>
ColorBurn,
/// <summary>
/// Darkens or lightens the colors, depending on the source color.
/// </summary>
SoftLight,
/// <summary>
/// Subtracts the darker color from the lighter color.
/// </summary>
Difference,
/// <summary>
/// Produces an effect similar to Difference with lower contrast.
/// </summary>
Exclusion,
/// <summary>
/// Uses the hue of the source color with the saturation and luminosity of the backdrop color.
/// </summary>
Hue,
/// <summary>
/// Uses the saturation of the source color with the hue and luminosity of the backdrop color.
/// </summary>
Saturation,
/// <summary>
/// Uses the hue and saturation of the source color with the luminosity of the backdrop color.
/// </summary>
Color,
/// <summary>
/// Uses the luminosity of the source color with the hue and saturation of the backdrop color.
/// </summary>
Luminosity,
}

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

@ -42,6 +42,15 @@ public partial class PixelOperations<TPixel>
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenClear;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayClear;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightClear;
case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders<TPixel>.ColorDodgeClear;
case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders<TPixel>.ColorBurnClear;
case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders<TPixel>.SoftLightClear;
case PixelColorBlendingMode.Difference: return DefaultPixelBlenders<TPixel>.DifferenceClear;
case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders<TPixel>.ExclusionClear;
case PixelColorBlendingMode.Hue: return DefaultPixelBlenders<TPixel>.HueClear;
case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders<TPixel>.SaturationClear;
case PixelColorBlendingMode.Color: return DefaultPixelBlenders<TPixel>.ColorClear;
case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders<TPixel>.LuminosityClear;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalClear;
}
@ -57,6 +66,15 @@ public partial class PixelOperations<TPixel>
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenXor;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayXor;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightXor;
case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders<TPixel>.ColorDodgeXor;
case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders<TPixel>.ColorBurnXor;
case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders<TPixel>.SoftLightXor;
case PixelColorBlendingMode.Difference: return DefaultPixelBlenders<TPixel>.DifferenceXor;
case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders<TPixel>.ExclusionXor;
case PixelColorBlendingMode.Hue: return DefaultPixelBlenders<TPixel>.HueXor;
case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders<TPixel>.SaturationXor;
case PixelColorBlendingMode.Color: return DefaultPixelBlenders<TPixel>.ColorXor;
case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders<TPixel>.LuminosityXor;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalXor;
}
@ -72,6 +90,15 @@ public partial class PixelOperations<TPixel>
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrc;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrc;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrc;
case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders<TPixel>.ColorDodgeSrc;
case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders<TPixel>.ColorBurnSrc;
case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders<TPixel>.SoftLightSrc;
case PixelColorBlendingMode.Difference: return DefaultPixelBlenders<TPixel>.DifferenceSrc;
case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders<TPixel>.ExclusionSrc;
case PixelColorBlendingMode.Hue: return DefaultPixelBlenders<TPixel>.HueSrc;
case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders<TPixel>.SaturationSrc;
case PixelColorBlendingMode.Color: return DefaultPixelBlenders<TPixel>.ColorSrc;
case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders<TPixel>.LuminositySrc;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrc;
}
@ -87,6 +114,15 @@ public partial class PixelOperations<TPixel>
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcAtop;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcAtop;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcAtop;
case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders<TPixel>.ColorDodgeSrcAtop;
case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders<TPixel>.ColorBurnSrcAtop;
case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders<TPixel>.SoftLightSrcAtop;
case PixelColorBlendingMode.Difference: return DefaultPixelBlenders<TPixel>.DifferenceSrcAtop;
case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders<TPixel>.ExclusionSrcAtop;
case PixelColorBlendingMode.Hue: return DefaultPixelBlenders<TPixel>.HueSrcAtop;
case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders<TPixel>.SaturationSrcAtop;
case PixelColorBlendingMode.Color: return DefaultPixelBlenders<TPixel>.ColorSrcAtop;
case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders<TPixel>.LuminositySrcAtop;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcAtop;
}
@ -102,6 +138,15 @@ public partial class PixelOperations<TPixel>
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcIn;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcIn;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcIn;
case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders<TPixel>.ColorDodgeSrcIn;
case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders<TPixel>.ColorBurnSrcIn;
case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders<TPixel>.SoftLightSrcIn;
case PixelColorBlendingMode.Difference: return DefaultPixelBlenders<TPixel>.DifferenceSrcIn;
case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders<TPixel>.ExclusionSrcIn;
case PixelColorBlendingMode.Hue: return DefaultPixelBlenders<TPixel>.HueSrcIn;
case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders<TPixel>.SaturationSrcIn;
case PixelColorBlendingMode.Color: return DefaultPixelBlenders<TPixel>.ColorSrcIn;
case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders<TPixel>.LuminositySrcIn;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcIn;
}
@ -117,6 +162,15 @@ public partial class PixelOperations<TPixel>
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOut;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOut;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOut;
case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders<TPixel>.ColorDodgeSrcOut;
case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders<TPixel>.ColorBurnSrcOut;
case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders<TPixel>.SoftLightSrcOut;
case PixelColorBlendingMode.Difference: return DefaultPixelBlenders<TPixel>.DifferenceSrcOut;
case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders<TPixel>.ExclusionSrcOut;
case PixelColorBlendingMode.Hue: return DefaultPixelBlenders<TPixel>.HueSrcOut;
case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders<TPixel>.SaturationSrcOut;
case PixelColorBlendingMode.Color: return DefaultPixelBlenders<TPixel>.ColorSrcOut;
case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders<TPixel>.LuminositySrcOut;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcOut;
}
@ -132,6 +186,15 @@ public partial class PixelOperations<TPixel>
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDest;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDest;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDest;
case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders<TPixel>.ColorDodgeDest;
case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders<TPixel>.ColorBurnDest;
case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders<TPixel>.SoftLightDest;
case PixelColorBlendingMode.Difference: return DefaultPixelBlenders<TPixel>.DifferenceDest;
case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders<TPixel>.ExclusionDest;
case PixelColorBlendingMode.Hue: return DefaultPixelBlenders<TPixel>.HueDest;
case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders<TPixel>.SaturationDest;
case PixelColorBlendingMode.Color: return DefaultPixelBlenders<TPixel>.ColorDest;
case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders<TPixel>.LuminosityDest;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDest;
}
@ -147,6 +210,15 @@ public partial class PixelOperations<TPixel>
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestAtop;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestAtop;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestAtop;
case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders<TPixel>.ColorDodgeDestAtop;
case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders<TPixel>.ColorBurnDestAtop;
case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders<TPixel>.SoftLightDestAtop;
case PixelColorBlendingMode.Difference: return DefaultPixelBlenders<TPixel>.DifferenceDestAtop;
case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders<TPixel>.ExclusionDestAtop;
case PixelColorBlendingMode.Hue: return DefaultPixelBlenders<TPixel>.HueDestAtop;
case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders<TPixel>.SaturationDestAtop;
case PixelColorBlendingMode.Color: return DefaultPixelBlenders<TPixel>.ColorDestAtop;
case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders<TPixel>.LuminosityDestAtop;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestAtop;
}
@ -162,6 +234,15 @@ public partial class PixelOperations<TPixel>
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestIn;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestIn;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestIn;
case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders<TPixel>.ColorDodgeDestIn;
case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders<TPixel>.ColorBurnDestIn;
case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders<TPixel>.SoftLightDestIn;
case PixelColorBlendingMode.Difference: return DefaultPixelBlenders<TPixel>.DifferenceDestIn;
case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders<TPixel>.ExclusionDestIn;
case PixelColorBlendingMode.Hue: return DefaultPixelBlenders<TPixel>.HueDestIn;
case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders<TPixel>.SaturationDestIn;
case PixelColorBlendingMode.Color: return DefaultPixelBlenders<TPixel>.ColorDestIn;
case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders<TPixel>.LuminosityDestIn;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestIn;
}
@ -177,6 +258,15 @@ public partial class PixelOperations<TPixel>
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestOut;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestOut;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestOut;
case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders<TPixel>.ColorDodgeDestOut;
case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders<TPixel>.ColorBurnDestOut;
case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders<TPixel>.SoftLightDestOut;
case PixelColorBlendingMode.Difference: return DefaultPixelBlenders<TPixel>.DifferenceDestOut;
case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders<TPixel>.ExclusionDestOut;
case PixelColorBlendingMode.Hue: return DefaultPixelBlenders<TPixel>.HueDestOut;
case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders<TPixel>.SaturationDestOut;
case PixelColorBlendingMode.Color: return DefaultPixelBlenders<TPixel>.ColorDestOut;
case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders<TPixel>.LuminosityDestOut;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestOut;
}
@ -192,10 +282,43 @@ public partial class PixelOperations<TPixel>
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestOver;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestOver;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestOver;
case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders<TPixel>.ColorDodgeDestOver;
case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders<TPixel>.ColorBurnDestOver;
case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders<TPixel>.SoftLightDestOver;
case PixelColorBlendingMode.Difference: return DefaultPixelBlenders<TPixel>.DifferenceDestOver;
case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders<TPixel>.ExclusionDestOver;
case PixelColorBlendingMode.Hue: return DefaultPixelBlenders<TPixel>.HueDestOver;
case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders<TPixel>.SaturationDestOver;
case PixelColorBlendingMode.Color: return DefaultPixelBlenders<TPixel>.ColorDestOver;
case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders<TPixel>.LuminosityDestOver;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestOver;
}
case PixelAlphaCompositionMode.Plus:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyPlus;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddPlus;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractPlus;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenPlus;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenPlus;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenPlus;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayPlus;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightPlus;
case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders<TPixel>.ColorDodgePlus;
case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders<TPixel>.ColorBurnPlus;
case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders<TPixel>.SoftLightPlus;
case PixelColorBlendingMode.Difference: return DefaultPixelBlenders<TPixel>.DifferencePlus;
case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders<TPixel>.ExclusionPlus;
case PixelColorBlendingMode.Hue: return DefaultPixelBlenders<TPixel>.HuePlus;
case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders<TPixel>.SaturationPlus;
case PixelColorBlendingMode.Color: return DefaultPixelBlenders<TPixel>.ColorPlus;
case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders<TPixel>.LuminosityPlus;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalPlus;
}
case PixelAlphaCompositionMode.SrcOver:
default:
switch (colorMode)
@ -208,6 +331,15 @@ public partial class PixelOperations<TPixel>
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOver;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOver;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOver;
case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders<TPixel>.ColorDodgeSrcOver;
case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders<TPixel>.ColorBurnSrcOver;
case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders<TPixel>.SoftLightSrcOver;
case PixelColorBlendingMode.Difference: return DefaultPixelBlenders<TPixel>.DifferenceSrcOver;
case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders<TPixel>.ExclusionSrcOver;
case PixelColorBlendingMode.Hue: return DefaultPixelBlenders<TPixel>.HueSrcOver;
case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders<TPixel>.SaturationSrcOver;
case PixelColorBlendingMode.Color: return DefaultPixelBlenders<TPixel>.ColorSrcOver;
case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders<TPixel>.LuminositySrcOver;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcOver;
}

221
tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs

@ -50,7 +50,16 @@ public class PixelBlenderTests
DefaultPixelBlenders<Rgba32>.DarkenClear.GetType(),
DefaultPixelBlenders<Rgba32>.LightenClear.GetType(),
DefaultPixelBlenders<Rgba32>.OverlayClear.GetType(),
DefaultPixelBlenders<Rgba32>.HardLightClear.GetType());
DefaultPixelBlenders<Rgba32>.HardLightClear.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDodgeClear.GetType(),
DefaultPixelBlenders<Rgba32>.ColorBurnClear.GetType(),
DefaultPixelBlenders<Rgba32>.SoftLightClear.GetType(),
DefaultPixelBlenders<Rgba32>.DifferenceClear.GetType(),
DefaultPixelBlenders<Rgba32>.ExclusionClear.GetType(),
DefaultPixelBlenders<Rgba32>.HueClear.GetType(),
DefaultPixelBlenders<Rgba32>.SaturationClear.GetType(),
DefaultPixelBlenders<Rgba32>.ColorClear.GetType(),
DefaultPixelBlenders<Rgba32>.LuminosityClear.GetType());
AddBlenderModeMappings(
data,
@ -63,7 +72,38 @@ public class PixelBlenderTests
DefaultPixelBlenders<Rgba32>.DarkenXor.GetType(),
DefaultPixelBlenders<Rgba32>.LightenXor.GetType(),
DefaultPixelBlenders<Rgba32>.OverlayXor.GetType(),
DefaultPixelBlenders<Rgba32>.HardLightXor.GetType());
DefaultPixelBlenders<Rgba32>.HardLightXor.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDodgeXor.GetType(),
DefaultPixelBlenders<Rgba32>.ColorBurnXor.GetType(),
DefaultPixelBlenders<Rgba32>.SoftLightXor.GetType(),
DefaultPixelBlenders<Rgba32>.DifferenceXor.GetType(),
DefaultPixelBlenders<Rgba32>.ExclusionXor.GetType(),
DefaultPixelBlenders<Rgba32>.HueXor.GetType(),
DefaultPixelBlenders<Rgba32>.SaturationXor.GetType(),
DefaultPixelBlenders<Rgba32>.ColorXor.GetType(),
DefaultPixelBlenders<Rgba32>.LuminosityXor.GetType());
AddBlenderModeMappings(
data,
PixelAlphaCompositionMode.Plus,
DefaultPixelBlenders<Rgba32>.NormalPlus.GetType(),
DefaultPixelBlenders<Rgba32>.MultiplyPlus.GetType(),
DefaultPixelBlenders<Rgba32>.AddPlus.GetType(),
DefaultPixelBlenders<Rgba32>.SubtractPlus.GetType(),
DefaultPixelBlenders<Rgba32>.ScreenPlus.GetType(),
DefaultPixelBlenders<Rgba32>.DarkenPlus.GetType(),
DefaultPixelBlenders<Rgba32>.LightenPlus.GetType(),
DefaultPixelBlenders<Rgba32>.OverlayPlus.GetType(),
DefaultPixelBlenders<Rgba32>.HardLightPlus.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDodgePlus.GetType(),
DefaultPixelBlenders<Rgba32>.ColorBurnPlus.GetType(),
DefaultPixelBlenders<Rgba32>.SoftLightPlus.GetType(),
DefaultPixelBlenders<Rgba32>.DifferencePlus.GetType(),
DefaultPixelBlenders<Rgba32>.ExclusionPlus.GetType(),
DefaultPixelBlenders<Rgba32>.HuePlus.GetType(),
DefaultPixelBlenders<Rgba32>.SaturationPlus.GetType(),
DefaultPixelBlenders<Rgba32>.ColorPlus.GetType(),
DefaultPixelBlenders<Rgba32>.LuminosityPlus.GetType());
AddBlenderModeMappings(
data,
@ -76,7 +116,16 @@ public class PixelBlenderTests
DefaultPixelBlenders<Rgba32>.DarkenSrc.GetType(),
DefaultPixelBlenders<Rgba32>.LightenSrc.GetType(),
DefaultPixelBlenders<Rgba32>.OverlaySrc.GetType(),
DefaultPixelBlenders<Rgba32>.HardLightSrc.GetType());
DefaultPixelBlenders<Rgba32>.HardLightSrc.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDodgeSrc.GetType(),
DefaultPixelBlenders<Rgba32>.ColorBurnSrc.GetType(),
DefaultPixelBlenders<Rgba32>.SoftLightSrc.GetType(),
DefaultPixelBlenders<Rgba32>.DifferenceSrc.GetType(),
DefaultPixelBlenders<Rgba32>.ExclusionSrc.GetType(),
DefaultPixelBlenders<Rgba32>.HueSrc.GetType(),
DefaultPixelBlenders<Rgba32>.SaturationSrc.GetType(),
DefaultPixelBlenders<Rgba32>.ColorSrc.GetType(),
DefaultPixelBlenders<Rgba32>.LuminositySrc.GetType());
AddBlenderModeMappings(
data,
@ -89,7 +138,16 @@ public class PixelBlenderTests
DefaultPixelBlenders<Rgba32>.DarkenSrcAtop.GetType(),
DefaultPixelBlenders<Rgba32>.LightenSrcAtop.GetType(),
DefaultPixelBlenders<Rgba32>.OverlaySrcAtop.GetType(),
DefaultPixelBlenders<Rgba32>.HardLightSrcAtop.GetType());
DefaultPixelBlenders<Rgba32>.HardLightSrcAtop.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDodgeSrcAtop.GetType(),
DefaultPixelBlenders<Rgba32>.ColorBurnSrcAtop.GetType(),
DefaultPixelBlenders<Rgba32>.SoftLightSrcAtop.GetType(),
DefaultPixelBlenders<Rgba32>.DifferenceSrcAtop.GetType(),
DefaultPixelBlenders<Rgba32>.ExclusionSrcAtop.GetType(),
DefaultPixelBlenders<Rgba32>.HueSrcAtop.GetType(),
DefaultPixelBlenders<Rgba32>.SaturationSrcAtop.GetType(),
DefaultPixelBlenders<Rgba32>.ColorSrcAtop.GetType(),
DefaultPixelBlenders<Rgba32>.LuminositySrcAtop.GetType());
AddBlenderModeMappings(
data,
@ -102,7 +160,16 @@ public class PixelBlenderTests
DefaultPixelBlenders<Rgba32>.DarkenSrcIn.GetType(),
DefaultPixelBlenders<Rgba32>.LightenSrcIn.GetType(),
DefaultPixelBlenders<Rgba32>.OverlaySrcIn.GetType(),
DefaultPixelBlenders<Rgba32>.HardLightSrcIn.GetType());
DefaultPixelBlenders<Rgba32>.HardLightSrcIn.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDodgeSrcIn.GetType(),
DefaultPixelBlenders<Rgba32>.ColorBurnSrcIn.GetType(),
DefaultPixelBlenders<Rgba32>.SoftLightSrcIn.GetType(),
DefaultPixelBlenders<Rgba32>.DifferenceSrcIn.GetType(),
DefaultPixelBlenders<Rgba32>.ExclusionSrcIn.GetType(),
DefaultPixelBlenders<Rgba32>.HueSrcIn.GetType(),
DefaultPixelBlenders<Rgba32>.SaturationSrcIn.GetType(),
DefaultPixelBlenders<Rgba32>.ColorSrcIn.GetType(),
DefaultPixelBlenders<Rgba32>.LuminositySrcIn.GetType());
AddBlenderModeMappings(
data,
@ -115,7 +182,16 @@ public class PixelBlenderTests
DefaultPixelBlenders<Rgba32>.DarkenSrcOut.GetType(),
DefaultPixelBlenders<Rgba32>.LightenSrcOut.GetType(),
DefaultPixelBlenders<Rgba32>.OverlaySrcOut.GetType(),
DefaultPixelBlenders<Rgba32>.HardLightSrcOut.GetType());
DefaultPixelBlenders<Rgba32>.HardLightSrcOut.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDodgeSrcOut.GetType(),
DefaultPixelBlenders<Rgba32>.ColorBurnSrcOut.GetType(),
DefaultPixelBlenders<Rgba32>.SoftLightSrcOut.GetType(),
DefaultPixelBlenders<Rgba32>.DifferenceSrcOut.GetType(),
DefaultPixelBlenders<Rgba32>.ExclusionSrcOut.GetType(),
DefaultPixelBlenders<Rgba32>.HueSrcOut.GetType(),
DefaultPixelBlenders<Rgba32>.SaturationSrcOut.GetType(),
DefaultPixelBlenders<Rgba32>.ColorSrcOut.GetType(),
DefaultPixelBlenders<Rgba32>.LuminositySrcOut.GetType());
AddBlenderModeMappings(
data,
@ -128,7 +204,16 @@ public class PixelBlenderTests
DefaultPixelBlenders<Rgba32>.DarkenDest.GetType(),
DefaultPixelBlenders<Rgba32>.LightenDest.GetType(),
DefaultPixelBlenders<Rgba32>.OverlayDest.GetType(),
DefaultPixelBlenders<Rgba32>.HardLightDest.GetType());
DefaultPixelBlenders<Rgba32>.HardLightDest.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDodgeDest.GetType(),
DefaultPixelBlenders<Rgba32>.ColorBurnDest.GetType(),
DefaultPixelBlenders<Rgba32>.SoftLightDest.GetType(),
DefaultPixelBlenders<Rgba32>.DifferenceDest.GetType(),
DefaultPixelBlenders<Rgba32>.ExclusionDest.GetType(),
DefaultPixelBlenders<Rgba32>.HueDest.GetType(),
DefaultPixelBlenders<Rgba32>.SaturationDest.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDest.GetType(),
DefaultPixelBlenders<Rgba32>.LuminosityDest.GetType());
AddBlenderModeMappings(
data,
@ -141,7 +226,16 @@ public class PixelBlenderTests
DefaultPixelBlenders<Rgba32>.DarkenDestAtop.GetType(),
DefaultPixelBlenders<Rgba32>.LightenDestAtop.GetType(),
DefaultPixelBlenders<Rgba32>.OverlayDestAtop.GetType(),
DefaultPixelBlenders<Rgba32>.HardLightDestAtop.GetType());
DefaultPixelBlenders<Rgba32>.HardLightDestAtop.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDodgeDestAtop.GetType(),
DefaultPixelBlenders<Rgba32>.ColorBurnDestAtop.GetType(),
DefaultPixelBlenders<Rgba32>.SoftLightDestAtop.GetType(),
DefaultPixelBlenders<Rgba32>.DifferenceDestAtop.GetType(),
DefaultPixelBlenders<Rgba32>.ExclusionDestAtop.GetType(),
DefaultPixelBlenders<Rgba32>.HueDestAtop.GetType(),
DefaultPixelBlenders<Rgba32>.SaturationDestAtop.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDestAtop.GetType(),
DefaultPixelBlenders<Rgba32>.LuminosityDestAtop.GetType());
AddBlenderModeMappings(
data,
@ -154,7 +248,16 @@ public class PixelBlenderTests
DefaultPixelBlenders<Rgba32>.DarkenDestIn.GetType(),
DefaultPixelBlenders<Rgba32>.LightenDestIn.GetType(),
DefaultPixelBlenders<Rgba32>.OverlayDestIn.GetType(),
DefaultPixelBlenders<Rgba32>.HardLightDestIn.GetType());
DefaultPixelBlenders<Rgba32>.HardLightDestIn.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDodgeDestIn.GetType(),
DefaultPixelBlenders<Rgba32>.ColorBurnDestIn.GetType(),
DefaultPixelBlenders<Rgba32>.SoftLightDestIn.GetType(),
DefaultPixelBlenders<Rgba32>.DifferenceDestIn.GetType(),
DefaultPixelBlenders<Rgba32>.ExclusionDestIn.GetType(),
DefaultPixelBlenders<Rgba32>.HueDestIn.GetType(),
DefaultPixelBlenders<Rgba32>.SaturationDestIn.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDestIn.GetType(),
DefaultPixelBlenders<Rgba32>.LuminosityDestIn.GetType());
AddBlenderModeMappings(
data,
@ -167,7 +270,16 @@ public class PixelBlenderTests
DefaultPixelBlenders<Rgba32>.DarkenDestOut.GetType(),
DefaultPixelBlenders<Rgba32>.LightenDestOut.GetType(),
DefaultPixelBlenders<Rgba32>.OverlayDestOut.GetType(),
DefaultPixelBlenders<Rgba32>.HardLightDestOut.GetType());
DefaultPixelBlenders<Rgba32>.HardLightDestOut.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDodgeDestOut.GetType(),
DefaultPixelBlenders<Rgba32>.ColorBurnDestOut.GetType(),
DefaultPixelBlenders<Rgba32>.SoftLightDestOut.GetType(),
DefaultPixelBlenders<Rgba32>.DifferenceDestOut.GetType(),
DefaultPixelBlenders<Rgba32>.ExclusionDestOut.GetType(),
DefaultPixelBlenders<Rgba32>.HueDestOut.GetType(),
DefaultPixelBlenders<Rgba32>.SaturationDestOut.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDestOut.GetType(),
DefaultPixelBlenders<Rgba32>.LuminosityDestOut.GetType());
AddBlenderModeMappings(
data,
@ -180,7 +292,16 @@ public class PixelBlenderTests
DefaultPixelBlenders<Rgba32>.DarkenDestOver.GetType(),
DefaultPixelBlenders<Rgba32>.LightenDestOver.GetType(),
DefaultPixelBlenders<Rgba32>.OverlayDestOver.GetType(),
DefaultPixelBlenders<Rgba32>.HardLightDestOver.GetType());
DefaultPixelBlenders<Rgba32>.HardLightDestOver.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDodgeDestOver.GetType(),
DefaultPixelBlenders<Rgba32>.ColorBurnDestOver.GetType(),
DefaultPixelBlenders<Rgba32>.SoftLightDestOver.GetType(),
DefaultPixelBlenders<Rgba32>.DifferenceDestOver.GetType(),
DefaultPixelBlenders<Rgba32>.ExclusionDestOver.GetType(),
DefaultPixelBlenders<Rgba32>.HueDestOver.GetType(),
DefaultPixelBlenders<Rgba32>.SaturationDestOver.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDestOver.GetType(),
DefaultPixelBlenders<Rgba32>.LuminosityDestOver.GetType());
AddBlenderModeMappings(
data,
@ -193,7 +314,16 @@ public class PixelBlenderTests
DefaultPixelBlenders<Rgba32>.DarkenSrcOver.GetType(),
DefaultPixelBlenders<Rgba32>.LightenSrcOver.GetType(),
DefaultPixelBlenders<Rgba32>.OverlaySrcOver.GetType(),
DefaultPixelBlenders<Rgba32>.HardLightSrcOver.GetType());
DefaultPixelBlenders<Rgba32>.HardLightSrcOver.GetType(),
DefaultPixelBlenders<Rgba32>.ColorDodgeSrcOver.GetType(),
DefaultPixelBlenders<Rgba32>.ColorBurnSrcOver.GetType(),
DefaultPixelBlenders<Rgba32>.SoftLightSrcOver.GetType(),
DefaultPixelBlenders<Rgba32>.DifferenceSrcOver.GetType(),
DefaultPixelBlenders<Rgba32>.ExclusionSrcOver.GetType(),
DefaultPixelBlenders<Rgba32>.HueSrcOver.GetType(),
DefaultPixelBlenders<Rgba32>.SaturationSrcOver.GetType(),
DefaultPixelBlenders<Rgba32>.ColorSrcOver.GetType(),
DefaultPixelBlenders<Rgba32>.LuminositySrcOver.GetType());
return data;
}
@ -228,6 +358,51 @@ public class PixelBlenderTests
ExerciseAllAssociatedAlphaBlenderModeCombinations,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic);
[Theory]
[InlineData(PixelColorBlendingMode.ColorDodge)]
[InlineData(PixelColorBlendingMode.ColorBurn)]
[InlineData(PixelColorBlendingMode.SoftLight)]
[InlineData(PixelColorBlendingMode.Difference)]
[InlineData(PixelColorBlendingMode.Exclusion)]
[InlineData(PixelColorBlendingMode.Hue)]
[InlineData(PixelColorBlendingMode.Saturation)]
[InlineData(PixelColorBlendingMode.Color)]
[InlineData(PixelColorBlendingMode.Luminosity)]
public void NewSrcOverModesMatchAcrossAlphaRepresentations(PixelColorBlendingMode colorMode)
{
Rgba32 background = new(220, 80, 40, 160);
Rgba32 source = new(20, 180, 120, 96);
PixelBlender<Rgba32> straightBlender = PixelOperations<Rgba32>.Instance.GetPixelBlender(colorMode, PixelAlphaCompositionMode.SrcOver);
PixelBlender<Rgba32P> associatedBlender = PixelOperations<Rgba32P>.Instance.GetPixelBlender(colorMode, PixelAlphaCompositionMode.SrcOver);
Rgba32 straightResult = straightBlender.Blend(background, source, .625F);
Rgba32P expected = Rgba32P.FromRgba32(straightResult);
Rgba32P actual = associatedBlender.Blend(Rgba32P.FromRgba32(background), Rgba32P.FromRgba32(source), .625F);
// Associated storage introduces one extra byte quantization, but both paths must describe the same composite.
Assert.True(Math.Abs(expected.R - actual.R) <= 1, $"{colorMode}: expected {expected}, actual {actual}");
Assert.True(Math.Abs(expected.G - actual.G) <= 1, $"{colorMode}: expected {expected}, actual {actual}");
Assert.True(Math.Abs(expected.B - actual.B) <= 1, $"{colorMode}: expected {expected}, actual {actual}");
Assert.Equal(expected.A, actual.A);
}
[Fact]
public void PlusMatchesAcrossAlphaRepresentations()
{
Rgba32 background = new(220, 80, 40, 160);
Rgba32 source = new(20, 180, 120, 96);
PixelBlender<Rgba32> straightBlender = PixelOperations<Rgba32>.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.Plus);
PixelBlender<Rgba32P> associatedBlender = PixelOperations<Rgba32P>.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.Plus);
Rgba32P expected = Rgba32P.FromRgba32(straightBlender.Blend(background, source, .625F));
Rgba32P actual = associatedBlender.Blend(Rgba32P.FromRgba32(background), Rgba32P.FromRgba32(source), .625F);
Assert.True(Math.Abs(expected.R - actual.R) <= 1, $"expected {expected}, actual {actual}");
Assert.True(Math.Abs(expected.G - actual.G) <= 1, $"expected {expected}, actual {actual}");
Assert.True(Math.Abs(expected.B - actual.B) <= 1, $"expected {expected}, actual {actual}");
Assert.Equal(expected.A, actual.A);
}
[Fact]
public void AssociatedHardLightDestAtopRoundsExactMidpointAwayFromZero() =>
FeatureTestRunner.RunWithHwIntrinsicsFeature(
@ -691,6 +866,8 @@ public class PixelBlenderTests
{ Color.MistyRose.ToPixel<Rgba32>(), Color.MidnightBlue.ToPixel<Rgba32>(), 1, PixelAlphaCompositionMode.SrcIn, Color.MidnightBlue.ToPixel<Rgba32>() },
{ Color.MistyRose.ToPixel<Rgba32>(), Color.MidnightBlue.ToPixel<Rgba32>(), 1, PixelAlphaCompositionMode.SrcOut, new Rgba32(0) },
{ Color.MistyRose.ToPixel<Rgba32>(), Color.MidnightBlue.ToPixel<Rgba32>(), 1, PixelAlphaCompositionMode.SrcOver, Color.MidnightBlue.ToPixel<Rgba32>() },
{ new Rgba32(51, 102, 153, 77), new Rgba32(204, 51, 26, 102), .5F, PixelAlphaCompositionMode.Plus, new Rgba32(112, 82, 102, 128) },
{ new Rgba32(230, 51, 26, 191), new Rgba32(204, 230, 77, 191), 1F, PixelAlphaCompositionMode.Plus, new Rgba32(255, 210, 77, 255) },
};
[Theory]
@ -716,7 +893,16 @@ public class PixelBlenderTests
Type darken,
Type lighten,
Type overlay,
Type hardLight)
Type hardLight,
Type colorDodge,
Type colorBurn,
Type softLight,
Type difference,
Type exclusion,
Type hue,
Type saturation,
Type color,
Type luminosity)
{
data.Add(PixelColorBlendingMode.Normal, alphaMode, normal);
data.Add(PixelColorBlendingMode.Multiply, alphaMode, multiply);
@ -727,6 +913,15 @@ public class PixelBlenderTests
data.Add(PixelColorBlendingMode.Lighten, alphaMode, lighten);
data.Add(PixelColorBlendingMode.Overlay, alphaMode, overlay);
data.Add(PixelColorBlendingMode.HardLight, alphaMode, hardLight);
data.Add(PixelColorBlendingMode.ColorDodge, alphaMode, colorDodge);
data.Add(PixelColorBlendingMode.ColorBurn, alphaMode, colorBurn);
data.Add(PixelColorBlendingMode.SoftLight, alphaMode, softLight);
data.Add(PixelColorBlendingMode.Difference, alphaMode, difference);
data.Add(PixelColorBlendingMode.Exclusion, alphaMode, exclusion);
data.Add(PixelColorBlendingMode.Hue, alphaMode, hue);
data.Add(PixelColorBlendingMode.Saturation, alphaMode, saturation);
data.Add(PixelColorBlendingMode.Color, alphaMode, color);
data.Add(PixelColorBlendingMode.Luminosity, alphaMode, luminosity);
}
private static void ExerciseAllBlenderModeCombinations()

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

@ -4,6 +4,7 @@
using System.Numerics;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
using SixLabors.ImageSharp.Tests.TestUtilities;
@ -444,10 +445,136 @@ public class PorterDuffFunctionsTests
Assert.Equal(expected512, actual, FloatComparer);
}
public static TheoryData<PixelColorBlendingMode, TestVector4, TestVector4, TestVector4> ExtendedBlendFunctionData { get; } = new()
{
{ PixelColorBlendingMode.ColorDodge, CreateTestVector(0F, .25F, .8F, 1F), CreateTestVector(1F, .5F, .5F, 1F), CreateTestVector(0F, .5F, 1F, 1F) },
{ PixelColorBlendingMode.ColorBurn, CreateTestVector(1F, .4F, .75F, 1F), CreateTestVector(0F, 0F, .5F, 1F), CreateTestVector(1F, 0F, .5F, 1F) },
{ PixelColorBlendingMode.SoftLight, CreateTestVector(.25F, .64F, .16F, 1F), CreateTestVector(.75F, .75F, .25F, 1F), CreateTestVector(.375F, .72F, .0928F, 1F) },
{ PixelColorBlendingMode.SoftLight, CreateTestVector(.25F, .36F, .81F, 1F), CreateTestVector(.5F, .5F, .5F, 1F), CreateTestVector(.25F, .36F, .81F, 1F) },
{ PixelColorBlendingMode.Difference, CreateTestVector(.1F, .8F, .4F, 1F), CreateTestVector(.7F, .2F, .4F, 1F), CreateTestVector(.6F, .6F, 0F, 1F) },
{ PixelColorBlendingMode.Exclusion, CreateTestVector(.1F, .8F, .4F, 1F), CreateTestVector(.7F, .2F, .4F, 1F), CreateTestVector(.66F, .68F, .48F, 1F) },
{ PixelColorBlendingMode.Hue, CreateTestVector(.2F, .7F, .4F, 1F), CreateTestVector(.9F, .1F, .6F, 1F), CreateTestVector(.832625F, .332625F, .645125F, 1F) },
{ PixelColorBlendingMode.Saturation, CreateTestVector(.2F, .7F, .4F, 1F), CreateTestVector(.9F, .1F, .6F, 1F), CreateTestVector(.0098F, .8098F, .3298F, 1F) },
{ PixelColorBlendingMode.Color, CreateTestVector(.2F, .7F, .4F, 1F), CreateTestVector(.9F, .1F, .6F, 1F), CreateTestVector(1F, .234851485F, .713069307F, 1F) },
{ PixelColorBlendingMode.Luminosity, CreateTestVector(.2F, .7F, .4F, 1F), CreateTestVector(.9F, .1F, .6F, 1F), CreateTestVector(.078F, .578F, .278F, 1F) },
{ PixelColorBlendingMode.Hue, CreateTestVector(.5F, .5F, .5F, 1F), CreateTestVector(.9F, .2F, .6F, 1F), CreateTestVector(.5F, .5F, .5F, 1F) },
{ PixelColorBlendingMode.Saturation, CreateTestVector(.5F, .5F, .5F, 1F), CreateTestVector(.9F, .2F, .6F, 1F), CreateTestVector(.5F, .5F, .5F, 1F) },
};
[Theory]
[MemberData(nameof(ExtendedBlendFunctionData))]
public void ExtendedBlendFunction(PixelColorBlendingMode mode, TestVector4 back, TestVector4 source, TestVector4 expected)
{
Vector4 actual = InvokeExtendedBlend(mode, back, source, 1F);
VectorAssert.Equal(expected, actual, 4);
}
[Theory]
[MemberData(nameof(ExtendedBlendFunctionData))]
public void ExtendedBlendFunction256(PixelColorBlendingMode mode, TestVector4 back, TestVector4 source, TestVector4 expected)
{
if (!Avx.IsSupported)
{
return;
}
Vector256<float> actual = InvokeExtendedBlend(mode, Vector256.Create(back.AsVector().AsVector128(), back.AsVector().AsVector128()), Vector256.Create(source.AsVector().AsVector128(), source.AsVector().AsVector128()), Vector256.Create(1F));
Vector256<float> expectedVector = Vector256.Create(expected.AsVector().AsVector128(), expected.AsVector().AsVector128());
Assert.Equal(expectedVector, actual, FloatComparer);
}
[Theory]
[MemberData(nameof(ExtendedBlendFunctionData))]
public void ExtendedBlendFunction512(PixelColorBlendingMode mode, TestVector4 back, TestVector4 source, TestVector4 expected)
{
if (!Avx512F.IsSupported)
{
return;
}
Vector512<float> actual = InvokeExtendedBlend(mode, CreateVector512(back), CreateVector512(source), Vector512.Create(1F));
Assert.Equal(CreateVector512(expected), actual, FloatComparer);
}
/// <summary>
/// Creates serializable vector test data without relying on the legacy convenience constructor's channel assignments.
/// </summary>
/// <param name="x">The X component.</param>
/// <param name="y">The Y component.</param>
/// <param name="z">The Z component.</param>
/// <param name="w">The W component.</param>
/// <returns>The test vector.</returns>
private static TestVector4 CreateTestVector(float x, float y, float z, float w)
=> new() { X = x, Y = y, Z = z, W = w };
/// <summary>
/// Invokes a new blend mode through its straight-alpha source-over function.
/// </summary>
/// <param name="mode">The color blending mode.</param>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="amount">The source amount.</param>
/// <returns>The composition result.</returns>
private static Vector4 InvokeExtendedBlend(PixelColorBlendingMode mode, Vector4 backdrop, Vector4 source, float amount)
=> mode switch
{
PixelColorBlendingMode.ColorDodge => PorterDuffFunctions.ColorDodgeSrcOver(backdrop, source, amount),
PixelColorBlendingMode.ColorBurn => PorterDuffFunctions.ColorBurnSrcOver(backdrop, source, amount),
PixelColorBlendingMode.SoftLight => PorterDuffFunctions.SoftLightSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Difference => PorterDuffFunctions.DifferenceSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Exclusion => PorterDuffFunctions.ExclusionSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Hue => PorterDuffFunctions.HueSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Saturation => PorterDuffFunctions.SaturationSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Color => PorterDuffFunctions.ColorSrcOver(backdrop, source, amount),
_ => PorterDuffFunctions.LuminositySrcOver(backdrop, source, amount),
};
/// <inheritdoc cref="InvokeExtendedBlend(PixelColorBlendingMode, Vector4, Vector4, float)" />
private static Vector256<float> InvokeExtendedBlend(PixelColorBlendingMode mode, Vector256<float> backdrop, Vector256<float> source, Vector256<float> amount)
=> mode switch
{
PixelColorBlendingMode.ColorDodge => PorterDuffFunctions.ColorDodgeSrcOver(backdrop, source, amount),
PixelColorBlendingMode.ColorBurn => PorterDuffFunctions.ColorBurnSrcOver(backdrop, source, amount),
PixelColorBlendingMode.SoftLight => PorterDuffFunctions.SoftLightSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Difference => PorterDuffFunctions.DifferenceSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Exclusion => PorterDuffFunctions.ExclusionSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Hue => PorterDuffFunctions.HueSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Saturation => PorterDuffFunctions.SaturationSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Color => PorterDuffFunctions.ColorSrcOver(backdrop, source, amount),
_ => PorterDuffFunctions.LuminositySrcOver(backdrop, source, amount),
};
/// <inheritdoc cref="InvokeExtendedBlend(PixelColorBlendingMode, Vector4, Vector4, float)" />
private static Vector512<float> InvokeExtendedBlend(PixelColorBlendingMode mode, Vector512<float> backdrop, Vector512<float> source, Vector512<float> amount)
=> mode switch
{
PixelColorBlendingMode.ColorDodge => PorterDuffFunctions.ColorDodgeSrcOver(backdrop, source, amount),
PixelColorBlendingMode.ColorBurn => PorterDuffFunctions.ColorBurnSrcOver(backdrop, source, amount),
PixelColorBlendingMode.SoftLight => PorterDuffFunctions.SoftLightSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Difference => PorterDuffFunctions.DifferenceSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Exclusion => PorterDuffFunctions.ExclusionSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Hue => PorterDuffFunctions.HueSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Saturation => PorterDuffFunctions.SaturationSrcOver(backdrop, source, amount),
PixelColorBlendingMode.Color => PorterDuffFunctions.ColorSrcOver(backdrop, source, amount),
_ => PorterDuffFunctions.LuminositySrcOver(backdrop, source, amount),
};
private static Vector512<float> CreateVector512(TestVector4 vector)
=> Vector512.Create(
vector.X, vector.Y, vector.Z, vector.W,
vector.X, vector.Y, vector.Z, vector.W,
vector.X, vector.Y, vector.Z, vector.W,
vector.X, vector.Y, vector.Z, vector.W);
vector.X,
vector.Y,
vector.Z,
vector.W,
vector.X,
vector.Y,
vector.Z,
vector.W,
vector.X,
vector.Y,
vector.Z,
vector.W,
vector.X,
vector.Y,
vector.Z,
vector.W);
}

Loading…
Cancel
Save