mirror of https://github.com/SixLabors/ImageSharp
12 changed files with 648 additions and 318 deletions
@ -1,274 +1,242 @@ |
|||||
// <copyright file="PorterDuffFunctions.cs" company="James Jackson-South">
|
// <copyright file="PorterDuffFunctions.cs" company="James Jackson-South">
|
||||
// Copyright (c) James Jackson-South and contributors.
|
// Copyright (c) James Jackson-South and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
// </copyright>
|
||||
|
|
||||
namespace ImageSharp.PixelFormats |
namespace ImageSharp.PixelFormats.PixelBlenders |
||||
{ |
{ |
||||
using System.Numerics; |
using System.Numerics; |
||||
using System.Runtime.CompilerServices; |
using System.Runtime.CompilerServices; |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Collection of Porter Duff alpha blending functions
|
/// Collection of Porter Duff alpha blending functions
|
||||
/// </summary>
|
/// </summary>
|
||||
/// <typeparam name="TPixel">Pixel Format</typeparam>
|
/// <remarks>
|
||||
/// <remarks>
|
/// These functions are designed to be a general solution for all color cases,
|
||||
/// These functions are designed to be a general solution for all color cases,
|
/// that is, they take in account the alpha value of both the backdrop
|
||||
/// that is, they take in account the alpha value of both the backdrop
|
/// and source, and there's no need to alpha-premultiply neither the backdrop
|
||||
/// and source, and there's no need to alpha-premultiply neither the backdrop
|
/// nor the source.
|
||||
/// nor the source.
|
/// Note there are faster functions for when the backdrop color is known
|
||||
/// Note there are faster functions for when the backdrop color is known
|
/// to be opaque
|
||||
/// to be opaque
|
/// </remarks>
|
||||
/// </remarks>
|
internal static class PorterDuffFunctions |
||||
internal static class PorterDuffFunctions<TPixel> |
{ |
||||
where TPixel : IPixel |
/// <summary>
|
||||
{ |
/// Source over backdrop
|
||||
/// <summary>
|
/// </summary>
|
||||
/// Source over backdrop
|
/// <param name="backdrop">Backgrop color</param>
|
||||
/// </summary>
|
/// <param name="source">Source color</param>
|
||||
/// <param name="backdrop">Backgrop color</param>
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
/// <param name="source">Source color</param>
|
/// <returns>Output color</returns>
|
||||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
/// <returns>Output color</returns>
|
public static Vector4 NormalBlendFunction(Vector4 backdrop, Vector4 source, float opacity) |
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
{ |
||||
public static TPixel NormalBlendFunction(TPixel backdrop, TPixel source, float opacity) |
source.W *= opacity; |
||||
{ |
if (source.W == 0) |
||||
Vector4 l = source.ToVector4(); |
{ |
||||
l.W *= opacity; |
return backdrop; |
||||
if (l.W == 0) |
} |
||||
{ |
|
||||
return backdrop; |
return Compose(backdrop, source, source); |
||||
} |
} |
||||
|
|
||||
Vector4 b = backdrop.ToVector4(); |
/// <summary>
|
||||
|
/// Source multiplied by backdrop
|
||||
return Compose(b, l, l); |
/// </summary>
|
||||
} |
/// <param name="backdrop">Backgrop color</param>
|
||||
|
/// <param name="source">Source color</param>
|
||||
/// <summary>
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
/// Source multiplied by backdrop
|
/// <returns>Output color</returns>
|
||||
/// </summary>
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
/// <param name="backdrop">Backgrop color</param>
|
public static Vector4 MultiplyFunction(Vector4 backdrop, Vector4 source, float opacity) |
||||
/// <param name="source">Source color</param>
|
{ |
||||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
source.W *= opacity; |
||||
/// <returns>Output color</returns>
|
if (source.W == 0) |
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
{ |
||||
public static TPixel MultiplyFunction(TPixel backdrop, TPixel source, float opacity) |
return backdrop; |
||||
{ |
} |
||||
Vector4 l = source.ToVector4(); |
|
||||
l.W *= opacity; |
return Compose(backdrop, source, backdrop * source); |
||||
if (l.W == 0) |
} |
||||
{ |
|
||||
return backdrop; |
/// <summary>
|
||||
} |
/// Source added to backdrop
|
||||
|
/// </summary>
|
||||
Vector4 b = backdrop.ToVector4(); |
/// <param name="backdrop">Backgrop color</param>
|
||||
|
/// <param name="source">Source color</param>
|
||||
return Compose(b, l, b * l); |
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
} |
/// <returns>Output color</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
/// <summary>
|
public static Vector4 AddFunction(Vector4 backdrop, Vector4 source, float opacity) |
||||
/// Source added to backdrop
|
{ |
||||
/// </summary>
|
source.W *= opacity; |
||||
/// <param name="backdrop">Backgrop color</param>
|
if (source.W == 0) |
||||
/// <param name="source">Source color</param>
|
{ |
||||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
return backdrop; |
||||
/// <returns>Output color</returns>
|
} |
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public static TPixel AddFunction(TPixel backdrop, TPixel source, float opacity) |
return Compose(backdrop, source, Vector4.Min(Vector4.One, backdrop + source)); |
||||
{ |
} |
||||
Vector4 l = source.ToVector4(); |
|
||||
l.W *= opacity; |
/// <summary>
|
||||
if (l.W == 0) |
/// Source substracted from backdrop
|
||||
{ |
/// </summary>
|
||||
return backdrop; |
/// <param name="backdrop">Backgrop color</param>
|
||||
} |
/// <param name="source">Source color</param>
|
||||
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
Vector4 b = backdrop.ToVector4(); |
/// <returns>Output color</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
return Compose(b, l, Vector4.Min(Vector4.One, b + l)); |
public static Vector4 SubstractFunction(Vector4 backdrop, Vector4 source, float opacity) |
||||
} |
{ |
||||
|
source.W *= opacity; |
||||
/// <summary>
|
if (source.W == 0) |
||||
/// Source substracted from backdrop
|
{ |
||||
/// </summary>
|
return backdrop; |
||||
/// <param name="backdrop">Backgrop color</param>
|
} |
||||
/// <param name="source">Source color</param>
|
|
||||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
return Compose(backdrop, source, Vector4.Max(Vector4.Zero, backdrop - source)); |
||||
/// <returns>Output color</returns>
|
} |
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public static TPixel SubstractFunction(TPixel backdrop, TPixel source, float opacity) |
/// <summary>
|
||||
{ |
/// Complement of source multiplied by the complement of backdrop
|
||||
Vector4 l = source.ToVector4(); |
/// </summary>
|
||||
l.W *= opacity; |
/// <param name="backdrop">Backgrop color</param>
|
||||
if (l.W == 0) |
/// <param name="source">Source color</param>
|
||||
{ |
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
return backdrop; |
/// <returns>Output color</returns>
|
||||
} |
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static Vector4 ScreenFunction(Vector4 backdrop, Vector4 source, float opacity) |
||||
Vector4 b = backdrop.ToVector4(); |
{ |
||||
|
source.W *= opacity; |
||||
return Compose(b, l, Vector4.Max(Vector4.Zero, b - l)); |
if (source.W == 0) |
||||
} |
{ |
||||
|
return backdrop; |
||||
/// <summary>
|
} |
||||
/// Complement of source multiplied by the complement of backdrop
|
|
||||
/// </summary>
|
return Compose(backdrop, source, Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source))); |
||||
/// <param name="backdrop">Backgrop color</param>
|
} |
||||
/// <param name="source">Source color</param>
|
|
||||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
/// <summary>
|
||||
/// <returns>Output color</returns>
|
/// Per element, chooses the smallest value of source and backdrop
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
/// </summary>
|
||||
public static TPixel ScreenFunction(TPixel backdrop, TPixel source, float opacity) |
/// <param name="backdrop">Backgrop color</param>
|
||||
{ |
/// <param name="source">Source color</param>
|
||||
Vector4 l = source.ToVector4(); |
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
l.W *= opacity; |
/// <returns>Output color</returns>
|
||||
if (l.W == 0) |
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
{ |
public static Vector4 DarkenFunction(Vector4 backdrop, Vector4 source, float opacity) |
||||
return backdrop; |
{ |
||||
} |
source.W *= opacity; |
||||
|
if (source.W == 0) |
||||
Vector4 b = backdrop.ToVector4(); |
{ |
||||
|
return backdrop; |
||||
return Compose(b, l, Vector4.One - ((Vector4.One - b) * (Vector4.One - l))); |
} |
||||
} |
|
||||
|
return Compose(backdrop, source, Vector4.Min(backdrop, source)); |
||||
/// <summary>
|
} |
||||
/// Per element, chooses the smallest value of source and backdrop
|
|
||||
/// </summary>
|
/// <summary>
|
||||
/// <param name="backdrop">Backgrop color</param>
|
/// Per element, chooses the largest value of source and backdrop
|
||||
/// <param name="source">Source color</param>
|
/// </summary>
|
||||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
/// <param name="backdrop">Backgrop color</param>
|
||||
/// <returns>Output color</returns>
|
/// <param name="source">Source color</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
public static TPixel DarkenFunction(TPixel backdrop, TPixel source, float opacity) |
/// <returns>Output color</returns>
|
||||
{ |
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
Vector4 l = source.ToVector4(); |
public static Vector4 LightenFunction(Vector4 backdrop, Vector4 source, float opacity) |
||||
l.W *= opacity; |
{ |
||||
if (l.W == 0) |
source.W *= opacity; |
||||
{ |
if (source.W == 0) |
||||
return backdrop; |
{ |
||||
} |
return backdrop; |
||||
|
} |
||||
Vector4 b = backdrop.ToVector4(); |
|
||||
|
return Compose(backdrop, source, Vector4.Max(backdrop, source)); |
||||
return Compose(b, l, Vector4.Min(b, l)); |
} |
||||
} |
|
||||
|
/// <summary>
|
||||
/// <summary>
|
/// Overlays source over backdrop
|
||||
/// Per element, chooses the largest value of source and backdrop
|
/// </summary>
|
||||
/// </summary>
|
/// <param name="backdrop">Backgrop color</param>
|
||||
/// <param name="backdrop">Backgrop color</param>
|
/// <param name="source">Source color</param>
|
||||
/// <param name="source">Source color</param>
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
/// <returns>Output color</returns>
|
||||
/// <returns>Output color</returns>
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
public static Vector4 OverlayFunction(Vector4 backdrop, Vector4 source, float opacity) |
||||
public static TPixel LightenFunction(TPixel backdrop, TPixel source, float opacity) |
{ |
||||
{ |
source.W *= opacity; |
||||
Vector4 l = source.ToVector4(); |
if (source.W == 0) |
||||
l.W *= opacity; |
{ |
||||
if (l.W == 0) |
return backdrop; |
||||
{ |
} |
||||
return backdrop; |
|
||||
} |
float cr = OverlayValueFunction(backdrop.X, source.X); |
||||
|
float cg = OverlayValueFunction(backdrop.Y, source.Y); |
||||
Vector4 b = backdrop.ToVector4(); |
float cb = OverlayValueFunction(backdrop.Z, source.Z); |
||||
|
|
||||
return Compose(b, l, Vector4.Max(b, l)); |
return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); |
||||
} |
} |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Overlays source over backdrop
|
/// Hard light effect
|
||||
/// </summary>
|
/// </summary>
|
||||
/// <param name="backdrop">Backgrop color</param>
|
/// <param name="backdrop">Backgrop color</param>
|
||||
/// <param name="source">Source color</param>
|
/// <param name="source">Source color</param>
|
||||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
/// <returns>Output color</returns>
|
/// <returns>Output color</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
public static TPixel OverlayFunction(TPixel backdrop, TPixel source, float opacity) |
public static Vector4 HardLightFunction(Vector4 backdrop, Vector4 source, float opacity) |
||||
{ |
{ |
||||
Vector4 l = source.ToVector4(); |
source.W *= opacity; |
||||
l.W *= opacity; |
if (source.W == 0) |
||||
if (l.W == 0) |
{ |
||||
{ |
return backdrop; |
||||
return backdrop; |
} |
||||
} |
|
||||
|
float cr = OverlayValueFunction(source.X, backdrop.X); |
||||
Vector4 b = backdrop.ToVector4(); |
float cg = OverlayValueFunction(source.Y, backdrop.Y); |
||||
|
float cb = OverlayValueFunction(source.Z, backdrop.Z); |
||||
float cr = OverlayValueFunction(b.X, l.X); |
|
||||
float cg = OverlayValueFunction(b.Y, l.Y); |
return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); |
||||
float cb = OverlayValueFunction(b.Z, l.Z); |
} |
||||
|
|
||||
return Compose(b, l, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); |
/// <summary>
|
||||
} |
/// Helper function for Overlay andHardLight modes
|
||||
|
/// </summary>
|
||||
/// <summary>
|
/// <param name="backdrop">Backdrop color element</param>
|
||||
/// Hard light effect
|
/// <param name="source">Source color element</param>
|
||||
/// </summary>
|
/// <returns>Overlay value</returns>
|
||||
/// <param name="backdrop">Backgrop color</param>
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
/// <param name="source">Source color</param>
|
private static float OverlayValueFunction(float backdrop, float source) |
||||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
{ |
||||
/// <returns>Output color</returns>
|
return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); |
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
} |
||||
public static TPixel HardLightFunction(TPixel backdrop, TPixel source, float opacity) |
|
||||
{ |
/// <summary>
|
||||
Vector4 l = source.ToVector4(); |
/// General composition function for all modes, with a general solution for alpha channel
|
||||
l.W *= opacity; |
/// </summary>
|
||||
if (l.W == 0) |
/// <param name="backdrop">Original backgrop color</param>
|
||||
{ |
/// <param name="source">Original source color</param>
|
||||
return backdrop; |
/// <param name="xform">Desired transformed color, without taking Alpha channel in account</param>
|
||||
} |
/// <returns>The final color</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
Vector4 b = backdrop.ToVector4(); |
private static Vector4 Compose(Vector4 backdrop, Vector4 source, Vector4 xform) |
||||
|
{ |
||||
float cr = OverlayValueFunction(l.X, b.X); |
DebugGuard.MustBeGreaterThan(source.W, 0, nameof(source.W)); |
||||
float cg = OverlayValueFunction(l.Y, b.Y); |
|
||||
float cb = OverlayValueFunction(l.Z, b.Z); |
// calculate weights
|
||||
|
float xw = backdrop.W * source.W; |
||||
return Compose(b, l, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); |
float bw = backdrop.W - xw; |
||||
} |
float sw = source.W - xw; |
||||
|
|
||||
/// <summary>
|
// calculate final alpha
|
||||
/// Helper function for Overlay andHardLight modes
|
float a = xw + bw + sw; |
||||
/// </summary>
|
|
||||
/// <param name="backdrop">Backdrop color element</param>
|
// calculate final value
|
||||
/// <param name="source">Source color element</param>
|
xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / a; |
||||
/// <returns>Overlay value</returns>
|
xform.W = a; |
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static float OverlayValueFunction(float backdrop, float source) |
return xform; |
||||
{ |
} |
||||
return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); |
} |
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// General composition function for all modes, with a general solution for alpha channel
|
|
||||
/// </summary>
|
|
||||
/// <param name="backdrop">Original backgrop color</param>
|
|
||||
/// <param name="source">Original source color</param>
|
|
||||
/// <param name="xform">Desired transformed color, without taking Alpha channel in account</param>
|
|
||||
/// <returns>The final color</returns>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static TPixel Compose(Vector4 backdrop, Vector4 source, Vector4 xform) |
|
||||
{ |
|
||||
DebugGuard.MustBeGreaterThan(source.W, 0, nameof(source.W)); |
|
||||
|
|
||||
// calculate weights
|
|
||||
float xw = backdrop.W * source.W; |
|
||||
float bw = backdrop.W - xw; |
|
||||
float sw = source.W - xw; |
|
||||
|
|
||||
// calculate final alpha
|
|
||||
float a = xw + bw + sw; |
|
||||
|
|
||||
// calculate final value
|
|
||||
xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / a; |
|
||||
xform.W = a; |
|
||||
|
|
||||
TPixel packed = default(TPixel); |
|
||||
packed.PackFromVector4(xform); |
|
||||
|
|
||||
return packed; |
|
||||
} |
|
||||
} |
|
||||
} |
} |
||||
@ -0,0 +1,151 @@ |
|||||
|
// <copyright file="PorterDuffFunctions.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.PixelFormats.PixelBlenders |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Collection of Porter Duff alpha blending functions
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">Pixel Format</typeparam>
|
||||
|
/// <remarks>
|
||||
|
/// These functions are designed to be a general solution for all color cases,
|
||||
|
/// that is, they take in account the alpha value of both the backdrop
|
||||
|
/// and source, and there's no need to alpha-premultiply neither the backdrop
|
||||
|
/// nor the source.
|
||||
|
/// Note there are faster functions for when the backdrop color is known
|
||||
|
/// to be opaque
|
||||
|
/// </remarks>
|
||||
|
internal static class PorterDuffFunctions<TPixel> |
||||
|
where TPixel : IPixel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Source over backdrop
|
||||
|
/// </summary>
|
||||
|
/// <param name="backdrop">Backgrop color</param>
|
||||
|
/// <param name="source">Source color</param>
|
||||
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
|
/// <returns>Output color</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static TPixel NormalBlendFunction(TPixel backdrop, TPixel source, float opacity) |
||||
|
{ |
||||
|
return ToPixel(PorterDuffFunctions.NormalBlendFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Source multiplied by backdrop
|
||||
|
/// </summary>
|
||||
|
/// <param name="backdrop">Backgrop color</param>
|
||||
|
/// <param name="source">Source color</param>
|
||||
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
|
/// <returns>Output color</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static TPixel MultiplyFunction(TPixel backdrop, TPixel source, float opacity) |
||||
|
{ |
||||
|
return ToPixel(PorterDuffFunctions.MultiplyFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Source added to backdrop
|
||||
|
/// </summary>
|
||||
|
/// <param name="backdrop">Backgrop color</param>
|
||||
|
/// <param name="source">Source color</param>
|
||||
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
|
/// <returns>Output color</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static TPixel AddFunction(TPixel backdrop, TPixel source, float opacity) |
||||
|
{ |
||||
|
return ToPixel(PorterDuffFunctions.AddFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Source substracted from backdrop
|
||||
|
/// </summary>
|
||||
|
/// <param name="backdrop">Backgrop color</param>
|
||||
|
/// <param name="source">Source color</param>
|
||||
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
|
/// <returns>Output color</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static TPixel SubstractFunction(TPixel backdrop, TPixel source, float opacity) |
||||
|
{ |
||||
|
return ToPixel(PorterDuffFunctions.SubstractFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Complement of source multiplied by the complement of backdrop
|
||||
|
/// </summary>
|
||||
|
/// <param name="backdrop">Backgrop color</param>
|
||||
|
/// <param name="source">Source color</param>
|
||||
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
|
/// <returns>Output color</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static TPixel ScreenFunction(TPixel backdrop, TPixel source, float opacity) |
||||
|
{ |
||||
|
return ToPixel(PorterDuffFunctions.ScreenFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Per element, chooses the smallest value of source and backdrop
|
||||
|
/// </summary>
|
||||
|
/// <param name="backdrop">Backgrop color</param>
|
||||
|
/// <param name="source">Source color</param>
|
||||
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
|
/// <returns>Output color</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static TPixel DarkenFunction(TPixel backdrop, TPixel source, float opacity) |
||||
|
{ |
||||
|
return ToPixel(PorterDuffFunctions.DarkenFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Per element, chooses the largest value of source and backdrop
|
||||
|
/// </summary>
|
||||
|
/// <param name="backdrop">Backgrop color</param>
|
||||
|
/// <param name="source">Source color</param>
|
||||
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
|
/// <returns>Output color</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static TPixel LightenFunction(TPixel backdrop, TPixel source, float opacity) |
||||
|
{ |
||||
|
return ToPixel(PorterDuffFunctions.LightenFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Overlays source over backdrop
|
||||
|
/// </summary>
|
||||
|
/// <param name="backdrop">Backgrop color</param>
|
||||
|
/// <param name="source">Source color</param>
|
||||
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
|
/// <returns>Output color</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static TPixel OverlayFunction(TPixel backdrop, TPixel source, float opacity) |
||||
|
{ |
||||
|
return ToPixel(PorterDuffFunctions.OverlayFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Hard light effect
|
||||
|
/// </summary>
|
||||
|
/// <param name="backdrop">Backgrop color</param>
|
||||
|
/// <param name="source">Source color</param>
|
||||
|
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
||||
|
/// <returns>Output color</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static TPixel HardLightFunction(TPixel backdrop, TPixel source, float opacity) |
||||
|
{ |
||||
|
return ToPixel(PorterDuffFunctions.HardLightFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static TPixel ToPixel(Vector4 vector) |
||||
|
{ |
||||
|
TPixel p = default(TPixel); |
||||
|
p.PackFromVector4(vector); |
||||
|
return p; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,103 @@ |
|||||
|
// <copyright file="Crop.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.Benchmarks |
||||
|
{ |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
using ImageSharp.PixelFormats; |
||||
|
using ImageSharp.Drawing; |
||||
|
using ImageSharp.Processing.Processors; |
||||
|
using CoreImage = ImageSharp.Image; |
||||
|
using CoreSize = ImageSharp.Size; |
||||
|
using System.Numerics; |
||||
|
using ImageSharp.PixelFormats.PixelBlenders; |
||||
|
|
||||
|
public class PorterDuffBulkVsPixel : BenchmarkBase |
||||
|
{ |
||||
|
private void BulkVectorConvert<TPixel>(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); |
||||
|
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); |
||||
|
Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); |
||||
|
|
||||
|
using (Buffer<Vector4> buffer = new Buffer<Vector4>(destination.Length * 3)) |
||||
|
{ |
||||
|
BufferSpan<Vector4> destinationSpan = buffer.Slice(0, destination.Length); |
||||
|
BufferSpan<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length); |
||||
|
BufferSpan<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); |
||||
|
|
||||
|
PixelOperations<TPixel>.Instance.ToVector4(background, backgroundSpan, destination.Length); |
||||
|
PixelOperations<TPixel>.Instance.ToVector4(source, sourceSpan, destination.Length); |
||||
|
|
||||
|
for (int i = 0; i < destination.Length; i++) |
||||
|
{ |
||||
|
destinationSpan[i] = PorterDuffFunctions.NormalBlendFunction(backgroundSpan[i], sourceSpan[i], amount[i]); |
||||
|
} |
||||
|
|
||||
|
PixelOperations<TPixel>.Instance.PackFromVector4(destinationSpan, destination, destination.Length); |
||||
|
} |
||||
|
} |
||||
|
private void BulkPixelConvert<TPixel>(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); |
||||
|
Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); |
||||
|
Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); |
||||
|
|
||||
|
for (int i = 0; i < destination.Length; i++) |
||||
|
{ |
||||
|
destination[i] = PorterDuffFunctions<TPixel>.NormalBlendFunction(destination[i], source[i], amount[i]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Description = "ImageSharp BulkVectorConvert")] |
||||
|
public CoreSize BulkVectorConvert() |
||||
|
{ |
||||
|
using (CoreImage image = new CoreImage(800, 800)) |
||||
|
{ |
||||
|
Buffer<float> amounts = new Buffer<float>(image.Width); |
||||
|
|
||||
|
for (int x = 0; x < image.Width; x++) |
||||
|
{ |
||||
|
amounts[x] = 1; |
||||
|
} |
||||
|
using (PixelAccessor<Rgba32> pixels = image.Lock()) |
||||
|
{ |
||||
|
for (int y = 0; y < image.Height; y++) |
||||
|
{ |
||||
|
BufferSpan<Rgba32> span = pixels.GetRowSpan(y); |
||||
|
BulkVectorConvert(span, span, span, amounts); |
||||
|
} |
||||
|
} |
||||
|
return new CoreSize(image.Width, image.Height); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Description = "ImageSharp BulkPixelConvert")] |
||||
|
public CoreSize BulkPixelConvert() |
||||
|
{ |
||||
|
using (CoreImage image = new CoreImage(800, 800)) |
||||
|
{ |
||||
|
Buffer<float> amounts = new Buffer<float>(image.Width); |
||||
|
|
||||
|
for (int x = 0; x < image.Width; x++) |
||||
|
{ |
||||
|
amounts[x] = 1; |
||||
|
} |
||||
|
using (PixelAccessor<Rgba32> pixels = image.Lock()) |
||||
|
{ |
||||
|
for (int y = 0; y < image.Height; y++) |
||||
|
{ |
||||
|
BufferSpan<Rgba32> span = pixels.GetRowSpan(y); |
||||
|
BulkPixelConvert(span, span, span, amounts); |
||||
|
} |
||||
|
} |
||||
|
return new CoreSize(image.Width, image.Height); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue