diff --git a/src/ImageSharp/PixelFormats/PixelTransformMode.cs b/src/ImageSharp/PixelFormats/PixelTransformMode.cs
new file mode 100644
index 000000000..7ac7eb029
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelTransformMode.cs
@@ -0,0 +1,58 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats
+{
+ ///
+ /// Porter Duff Blending composition modes
+ ///
+ public enum PixelTransformMode
+ {
+ ///
+ /// Default blending mode, also known as "Normal" or "Alpha Blending"
+ ///
+ Normal,
+
+ ///
+ /// Backdrop + Source
+ ///
+ Multiply,
+
+ ///
+ /// Backdrop + Source
+ ///
+ Add,
+
+ ///
+ /// Backdrop - Source
+ ///
+ Substract,
+
+ ///
+ /// Screen effect
+ ///
+ Screen,
+
+ ///
+ /// Darken effect
+ ///
+ Darken,
+
+ ///
+ /// Lighten effect
+ ///
+ Lighten,
+
+ ///
+ /// Overlay effect
+ ///
+ Overlay,
+
+ ///
+ /// Hard light effect
+ ///
+ HardLight
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs b/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs
new file mode 100644
index 000000000..199224462
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs
@@ -0,0 +1,56 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+ using PixelFormats;
+
+ ///
+ /// Extensions to retrieve the appropiate pixel transformation functions for
+ ///
+ public static class PixelTransformModeExtensions
+ {
+ ///
+ /// Gets a pixel transformation function
+ ///
+ /// The pixel format used for both Backdrop and Source
+ /// The Duff Porter mode
+ /// A function that transforms a Backdrop and Source colors into a final color
+ public static Func GetPixelFunction(this PixelTransformMode mode)
+ where TPixel : IPixel
+ {
+ return mode.GetPixelFunction();
+ }
+
+ ///
+ /// Gets a pixel transformation function
+ ///
+ /// The pixel format used for Backdrop and Output
+ /// The pixel format used for Source
+ /// The Duff Porter mode
+ /// A function that transforms a Backdrop and Source colors into a final color
+ public static Func GetPixelFunction(this PixelTransformMode mode)
+ where TBckPixel : IPixel
+ where TSrcPixel : IPixel
+ {
+ switch (mode)
+ {
+ case PixelTransformMode.Normal: return PorterDuffFunctions.NormalBlendFunction;
+ case PixelTransformMode.Multiply: return PorterDuffFunctions.MultiplyFunction;
+ case PixelTransformMode.Add: return PorterDuffFunctions.AddFunction;
+ case PixelTransformMode.Screen: return PorterDuffFunctions.ScreenFunction;
+ case PixelTransformMode.Darken: return PorterDuffFunctions.DarkenFunction;
+ case PixelTransformMode.Lighten: return PorterDuffFunctions.LightenFunction;
+ case PixelTransformMode.Overlay: return PorterDuffFunctions.OverlayFunction;
+ case PixelTransformMode.HardLight: return PorterDuffFunctions.HardLightFunction;
+
+ default: throw new NotImplementedException(nameof(mode));
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs
new file mode 100644
index 000000000..0e9b985e8
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs
@@ -0,0 +1,267 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats
+{
+ using System.Numerics;
+ using System.Runtime.CompilerServices;
+
+ ///
+ /// Collection of Porter Duff alpha blending functions
+ ///
+ /// Backdrop Pixel Format
+ /// Source Pixel Format
+ ///
+ /// 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
+ ///
+ internal static class PorterDuffFunctions
+ where TBckPixel : IPixel
+ where TsrcPixel : IPixel
+ {
+ ///
+ /// Source over backdrop
+ ///
+ /// Backgrop color
+ /// Source color
+ /// Opacity applied to Source Alpha
+ /// Output color
+ 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);
+ }
+
+ ///
+ /// Source multiplied by backdrop
+ ///
+ /// Backgrop color
+ /// Source color
+ /// Opacity applied to Source Alpha
+ /// Output color
+ 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);
+ }
+
+ ///
+ /// Source added to backdrop
+ ///
+ /// Backgrop color
+ /// Source color
+ /// Opacity applied to Source Alpha
+ /// Output color
+ 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));
+ }
+
+ ///
+ /// Source substracted from backdrop
+ ///
+ /// Backgrop color
+ /// Source color
+ /// Opacity applied to Source Alpha
+ /// Output color
+ 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));
+ }
+
+ ///
+ /// Complement of source multiplied by the complement of backdrop
+ ///
+ /// Backgrop color
+ /// Source color
+ /// Opacity applied to Source Alpha
+ /// Output color
+ 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)));
+ }
+
+ ///
+ /// Per element, chooses the smallest value of source and backdrop
+ ///
+ /// Backgrop color
+ /// Source color
+ /// Opacity applied to Source Alpha
+ /// Output color
+ 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));
+ }
+
+ ///
+ /// Per element, chooses the largest value of source and backdrop
+ ///
+ /// Backgrop color
+ /// Source color
+ /// Opacity applied to Source Alpha
+ /// Output color
+ 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));
+ }
+
+ ///
+ /// Overlays source over backdrop
+ ///
+ /// Backgrop color
+ /// Source color
+ /// Opacity applied to Source Alpha
+ /// Output color
+ 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)));
+ }
+
+ ///
+ /// Hard light effect
+ ///
+ /// Backgrop color
+ /// Source color
+ /// Opacity applied to Source Alpha
+ /// Output color
+ 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)));
+ }
+
+ ///
+ /// Helper function for Overlay andHardLight modes
+ ///
+ /// Backdrop color element
+ /// Source color element
+ /// Overlay value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static float OverlayValueFunction(float backdrop, float source)
+ {
+ return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop));
+ }
+
+ ///
+ /// General composition function for all modes, with a general solution for alpha channel
+ ///
+ /// Original backgrop color
+ /// Original source color
+ /// Desired transformed color, without taking Alpha channel in account
+ /// The final color
+ [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;
+ }
+ }
+}
\ No newline at end of file