mirror of https://github.com/SixLabors/ImageSharp
3 changed files with 381 additions and 0 deletions
@ -0,0 +1,58 @@ |
|||
// <copyright file="IPixel.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.PixelFormats |
|||
{ |
|||
/// <summary>
|
|||
/// Porter Duff Blending composition modes
|
|||
/// </summary>
|
|||
public enum PixelTransformMode |
|||
{ |
|||
/// <summary>
|
|||
/// Default blending mode, also known as "Normal" or "Alpha Blending"
|
|||
/// </summary>
|
|||
Normal, |
|||
|
|||
/// <summary>
|
|||
/// Backdrop + Source
|
|||
/// </summary>
|
|||
Multiply, |
|||
|
|||
/// <summary>
|
|||
/// Backdrop + Source
|
|||
/// </summary>
|
|||
Add, |
|||
|
|||
/// <summary>
|
|||
/// Backdrop - Source
|
|||
/// </summary>
|
|||
Substract, |
|||
|
|||
/// <summary>
|
|||
/// Screen effect
|
|||
/// </summary>
|
|||
Screen, |
|||
|
|||
/// <summary>
|
|||
/// Darken effect
|
|||
/// </summary>
|
|||
Darken, |
|||
|
|||
/// <summary>
|
|||
/// Lighten effect
|
|||
/// </summary>
|
|||
Lighten, |
|||
|
|||
/// <summary>
|
|||
/// Overlay effect
|
|||
/// </summary>
|
|||
Overlay, |
|||
|
|||
/// <summary>
|
|||
/// Hard light effect
|
|||
/// </summary>
|
|||
HardLight |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
// <copyright file="IPixel.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Extensions to retrieve the appropiate pixel transformation functions for <see cref="PixelTransformMode"/>
|
|||
/// </summary>
|
|||
public static class PixelTransformModeExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Gets a pixel transformation function
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format used for both Backdrop and Source</typeparam>
|
|||
/// <param name="mode">The Duff Porter mode</param>
|
|||
/// <returns>A function that transforms a Backdrop and Source colors into a final color</returns>
|
|||
public static Func<TPixel, TPixel, float, TPixel> GetPixelFunction<TPixel>(this PixelTransformMode mode) |
|||
where TPixel : IPixel |
|||
{ |
|||
return mode.GetPixelFunction<TPixel, TPixel>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a pixel transformation function
|
|||
/// </summary>
|
|||
/// <typeparam name="TBckPixel">The pixel format used for Backdrop and Output</typeparam>
|
|||
/// <typeparam name="TSrcPixel">The pixel format used for Source</typeparam>
|
|||
/// <param name="mode">The Duff Porter mode</param>
|
|||
/// <returns>A function that transforms a Backdrop and Source colors into a final color</returns>
|
|||
public static Func<TBckPixel, TSrcPixel, float, TBckPixel> GetPixelFunction<TBckPixel, TSrcPixel>(this PixelTransformMode mode) |
|||
where TBckPixel : IPixel |
|||
where TSrcPixel : IPixel |
|||
{ |
|||
switch (mode) |
|||
{ |
|||
case PixelTransformMode.Normal: return PorterDuffFunctions<TBckPixel, TSrcPixel>.NormalBlendFunction; |
|||
case PixelTransformMode.Multiply: return PorterDuffFunctions<TBckPixel, TSrcPixel>.MultiplyFunction; |
|||
case PixelTransformMode.Add: return PorterDuffFunctions<TBckPixel, TSrcPixel>.AddFunction; |
|||
case PixelTransformMode.Screen: return PorterDuffFunctions<TBckPixel, TSrcPixel>.ScreenFunction; |
|||
case PixelTransformMode.Darken: return PorterDuffFunctions<TBckPixel, TSrcPixel>.DarkenFunction; |
|||
case PixelTransformMode.Lighten: return PorterDuffFunctions<TBckPixel, TSrcPixel>.LightenFunction; |
|||
case PixelTransformMode.Overlay: return PorterDuffFunctions<TBckPixel, TSrcPixel>.OverlayFunction; |
|||
case PixelTransformMode.HardLight: return PorterDuffFunctions<TBckPixel, TSrcPixel>.HardLightFunction; |
|||
|
|||
default: throw new NotImplementedException(nameof(mode)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,267 @@ |
|||
// <copyright file="IPixel.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.PixelFormats |
|||
{ |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
/// <summary>
|
|||
/// Collection of Porter Duff alpha blending functions
|
|||
/// </summary>
|
|||
/// <typeparam name="TBckPixel">Backdrop Pixel Format</typeparam>
|
|||
/// <typeparam name="TsrcPixel">Source 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<TBckPixel, TsrcPixel> |
|||
where TBckPixel : IPixel |
|||
where TsrcPixel : 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>
|
|||
public static TBckPixel NormalBlendFunction(TBckPixel backdrop, TsrcPixel source, float opacity) |
|||
{ |
|||
Vector4 l = source.ToVector4(); |
|||
l.W *= opacity; |
|||
if (l.W == 0) |
|||
{ |
|||
return backdrop; |
|||
} |
|||
|
|||
Vector4 b = backdrop.ToVector4(); |
|||
|
|||
return Compose(b, l, l); |
|||
} |
|||
|
|||
/// <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>
|
|||
public static TBckPixel MultiplyFunction(TBckPixel backdrop, TsrcPixel source, float opacity) |
|||
{ |
|||
Vector4 l = source.ToVector4(); |
|||
l.W *= opacity; |
|||
if (l.W == 0) |
|||
{ |
|||
return backdrop; |
|||
} |
|||
|
|||
Vector4 b = backdrop.ToVector4(); |
|||
|
|||
return Compose(b, l, b * l); |
|||
} |
|||
|
|||
/// <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>
|
|||
public static TBckPixel AddFunction(TBckPixel backdrop, TsrcPixel source, float opacity) |
|||
{ |
|||
Vector4 l = source.ToVector4(); |
|||
l.W *= opacity; |
|||
if (l.W == 0) |
|||
{ |
|||
return backdrop; |
|||
} |
|||
|
|||
Vector4 b = backdrop.ToVector4(); |
|||
|
|||
return Compose(b, l, Vector4.Min(Vector4.One, b + l)); |
|||
} |
|||
|
|||
/// <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>
|
|||
public static TBckPixel SubstractFunction(TBckPixel backdrop, TsrcPixel source, float opacity) |
|||
{ |
|||
Vector4 l = source.ToVector4(); |
|||
l.W *= opacity; |
|||
if (l.W == 0) |
|||
{ |
|||
return backdrop; |
|||
} |
|||
|
|||
Vector4 b = backdrop.ToVector4(); |
|||
|
|||
return Compose(b, l, Vector4.Max(Vector4.Zero, b - l)); |
|||
} |
|||
|
|||
/// <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>
|
|||
public static TBckPixel ScreenFunction(TBckPixel backdrop, TsrcPixel source, float opacity) |
|||
{ |
|||
Vector4 l = source.ToVector4(); |
|||
l.W *= opacity; |
|||
if (l.W == 0) |
|||
{ |
|||
return backdrop; |
|||
} |
|||
|
|||
Vector4 b = backdrop.ToVector4(); |
|||
|
|||
return Compose(b, l, Vector4.One - ((Vector4.One - b) * (Vector4.One - l))); |
|||
} |
|||
|
|||
/// <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>
|
|||
public static TBckPixel DarkenFunction(TBckPixel backdrop, TsrcPixel source, float opacity) |
|||
{ |
|||
Vector4 l = source.ToVector4(); |
|||
l.W *= opacity; |
|||
if (l.W == 0) |
|||
{ |
|||
return backdrop; |
|||
} |
|||
|
|||
Vector4 b = backdrop.ToVector4(); |
|||
|
|||
return Compose(b, l, Vector4.Min(b, l)); |
|||
} |
|||
|
|||
/// <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>
|
|||
public static TBckPixel LightenFunction(TBckPixel backdrop, TsrcPixel source, float opacity) |
|||
{ |
|||
Vector4 l = source.ToVector4(); |
|||
l.W *= opacity; |
|||
if (l.W == 0) |
|||
{ |
|||
return backdrop; |
|||
} |
|||
|
|||
Vector4 b = backdrop.ToVector4(); |
|||
|
|||
return Compose(b, l, Vector4.Max(b, l)); |
|||
} |
|||
|
|||
/// <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>
|
|||
public static TBckPixel OverlayFunction(TBckPixel backdrop, TsrcPixel source, float opacity) |
|||
{ |
|||
Vector4 l = source.ToVector4(); |
|||
l.W *= opacity; |
|||
if (l.W == 0) |
|||
{ |
|||
return backdrop; |
|||
} |
|||
|
|||
Vector4 b = backdrop.ToVector4(); |
|||
|
|||
float cr = OverlayValueFunction(b.X, l.X); |
|||
float cg = OverlayValueFunction(b.Y, l.Y); |
|||
float cb = OverlayValueFunction(b.Z, l.Z); |
|||
|
|||
return Compose(b, l, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); |
|||
} |
|||
|
|||
/// <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>
|
|||
public static TBckPixel HardLightFunction(TBckPixel backdrop, TsrcPixel source, float opacity) |
|||
{ |
|||
Vector4 l = source.ToVector4(); |
|||
l.W *= opacity; |
|||
if (l.W == 0) |
|||
{ |
|||
return backdrop; |
|||
} |
|||
|
|||
Vector4 b = backdrop.ToVector4(); |
|||
|
|||
float cr = OverlayValueFunction(l.X, b.X); |
|||
float cg = OverlayValueFunction(l.Y, b.Y); |
|||
float cb = OverlayValueFunction(l.Z, b.Z); |
|||
|
|||
return Compose(b, l, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper function for Overlay andHardLight modes
|
|||
/// </summary>
|
|||
/// <param name="backdrop">Backdrop color element</param>
|
|||
/// <param name="source">Source color element</param>
|
|||
/// <returns>Overlay value</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static float OverlayValueFunction(float backdrop, float source) |
|||
{ |
|||
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 TBckPixel Compose(Vector4 backdrop, Vector4 source, Vector4 xform) |
|||
{ |
|||
System.Diagnostics.Debug.Assert(source.W > 0, nameof(source) + " Alpha must be non zero"); |
|||
|
|||
// 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; |
|||
|
|||
TBckPixel packed = default(TBckPixel); |
|||
packed.PackFromVector4(xform); |
|||
|
|||
return packed; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue