From 0384acb36fa90bd99e2f1837df48750e0965e408 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 17 Jun 2017 18:31:42 +0100 Subject: [PATCH 1/5] generate pixel blenders --- .editorconfig | 6 +- src/ImageSharp/ImageSharp.csproj | 18 + .../PixelFormats/PixelBlenderMode.cs | 62 +- .../DefaultAddPixelBlender{TPixel}.cs | 57 -- .../DefaultDarkenPixelBlender{TPixel}.cs | 57 -- .../DefaultHardLightPixelBlender{TPixel}.cs | 57 -- .../DefaultLightenPixelBlender{TPixel}.cs | 57 -- .../DefaultMultiplyPixelBlender{TPixel}.cs | 57 -- .../DefaultNormalPixelBlender{TPixel}.cs | 57 -- .../DefaultOverlayPixelBlender{TPixel}.cs | 57 -- .../DefaultPixelBlenders.Generated.cs | 849 ++++++++++++++++++ .../DefaultPixelBlenders.Generated.tt | 118 +++ .../DefaultScreenPixelBlender{TPixel}.cs | 57 -- .../DefaultSubstractPixelBlender{TPixel}.cs | 57 -- .../PorterDuffFunctions.Generated.cs | 314 +++++++ .../PorterDuffFunctions.Generated.tt | 126 +++ .../PixelBlenders/PorterDuffFunctions.cs | 20 +- .../PorterDuffFunctions{TPixel}.cs | 151 ---- .../PixelOperations{TPixel}.PixelBenders.cs | 39 +- .../PixelBlenders/PorterDuffBulkVsPixel.cs | 4 +- .../PixelBlenders/PorterDuffFunctionsTests.cs | 18 +- .../PorterDuffFunctionsTests_TPixel.cs | 54 +- .../PixelOperationsTests.Blender.cs | 66 +- 23 files changed, 1608 insertions(+), 750 deletions(-) delete mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs delete mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs delete mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs delete mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs delete mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs delete mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs delete mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt delete mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs delete mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt delete mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions{TPixel}.cs diff --git a/.editorconfig b/.editorconfig index fa43757a9..b725c5cce 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,4 +13,8 @@ dotnet_style_predefined_type_for_locals_parameters_members = true:warning dotnet_style_predefined_type_for_member_access = true:warning dotnet_style_qualification_for_field = true:warning dotnet_style_qualification_for_method = true:warning -dotnet_style_qualification_for_property = true:warning \ No newline at end of file +dotnet_style_qualification_for_property = true:warning + +[*.tt] +indent_style = space +indent_size = 4 \ No newline at end of file diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 17f7bf58f..3b72f12a8 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -63,6 +63,14 @@ TextTemplatingFileGenerator Rgba32.PixelOperations.Generated.cs + + PorterDuffFunctions.Generated.cs + TextTemplatingFileGenerator + + + DefaultPixelBlenders.Generated.cs + TextTemplatingFileGenerator + @@ -83,5 +91,15 @@ True Rgba32.PixelOperations.Generated.tt + + True + True + DefaultPixelBlenders.Generated.tt + + + True + True + PorterDuffFunctions.Generated.tt + \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs index d8031fe6e..1e48f7181 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs @@ -57,6 +57,66 @@ namespace ImageSharp.PixelFormats /// /// Multiplies or screens the colors, depending on the source value. /// - HardLight + HardLight, + + /// + /// returns the source colors + /// + Src, + + /// + /// returns the source over the destination + /// + Atop, + + /// + /// returns the detination over the source + /// + Over, + + /// + /// the source where the desitnation and source overlap + /// + In, + + /// + /// the destination where the desitnation and source overlap + /// + Out, + + /// + /// the destination where the source does not overlap it + /// + Dest, + + /// + /// the source where they dont overlap othersie dest in overlapping parts + /// + DestAtop, + + /// + /// the destnation over the source + /// + DestOver, + + /// + /// the destination where the desitnation and source overlap + /// + DestIn, + + /// + /// the source where the desitnation and source overlap + /// + DestOut, + + /// + /// the clear. + /// + Clear, + + /// + /// clear where they overlap + /// + Xor } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs deleted file mode 100644 index 261a98674..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Add" blending to pixels. - /// - /// The type of the pixel - internal class DefaultAddPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultAddPixelBlender Instance { get; } = new DefaultAddPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.AddFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - 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 buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.AddFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs deleted file mode 100644 index bca99e2f0..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Darken" blending to pixels. - /// - /// The type of the pixel - internal class DefaultDarkenPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultDarkenPixelBlender Instance { get; } = new DefaultDarkenPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.DarkenFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - 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 buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.DarkenFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs deleted file mode 100644 index 646423cff..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Hard Light" blending to pixels. - /// - /// The type of the pixel - internal class DefaultHardLightPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultHardLightPixelBlender Instance { get; } = new DefaultHardLightPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.HardLightFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - 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 buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.HardLightFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs deleted file mode 100644 index 55ad81e7a..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Lighten" blending to pixels. - /// - /// The type of the pixel - internal class DefaultLightenPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultLightenPixelBlender Instance { get; } = new DefaultLightenPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.LightenFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - 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 buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.LightenFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs deleted file mode 100644 index e21efaed0..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Multiply" blending to pixels. - /// - /// The type of the pixel - internal class DefaultMultiplyPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultMultiplyPixelBlender Instance { get; } = new DefaultMultiplyPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.MultiplyFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - 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 buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.MultiplyFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs deleted file mode 100644 index 9d63d11e0..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies a "Normal" otherwise nown as "Alpha Blending" blending to pixels. - /// - /// The type of the pixel - internal class DefaultNormalPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultNormalPixelBlender Instance { get; } = new DefaultNormalPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.NormalBlendFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - 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 buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.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.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs deleted file mode 100644 index 8172909ec..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Overlay" blending to pixels. - /// - /// The type of the pixel - internal class DefaultOverlayPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultOverlayPixelBlender Instance { get; } = new DefaultOverlayPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.OverlayFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - 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 buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.OverlayFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs new file mode 100644 index 000000000..915d9a924 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -0,0 +1,849 @@ +// +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.Memory; + + + /// + /// Collection of Porter Duff alpha blending functions applying different composition models. + /// + /// + /// 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 DefaultPixelBlenders + where TPixel : struct, IPixel + { + + internal class Normal : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Normal Instance { get; } = new Normal(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Normal(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Normal(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Multiply : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Multiply Instance { get; } = new Multiply(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Multiply(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Multiply(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Add : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Add Instance { get; } = new Add(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Add(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Add(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Substract : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Substract Instance { get; } = new Substract(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Substract(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Substract(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Screen : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Screen Instance { get; } = new Screen(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Screen(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Screen(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Darken : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Darken Instance { get; } = new Darken(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Darken(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Darken(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Lighten : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Lighten Instance { get; } = new Lighten(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Lighten(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Lighten(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Overlay : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Overlay Instance { get; } = new Overlay(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Overlay(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Overlay(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class HardLight : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static HardLight Instance { get; } = new HardLight(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.HardLight(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.HardLight(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Src : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Src Instance { get; } = new Src(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Src(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Src(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Atop : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Atop Instance { get; } = new Atop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Atop(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Atop(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Over : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Over Instance { get; } = new Over(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Over(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Over(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class In : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static In Instance { get; } = new In(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.In(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.In(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Out : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Out Instance { get; } = new Out(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Out(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Out(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Dest : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Dest Instance { get; } = new Dest(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Dest(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Dest(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class DestAtop : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static DestAtop Instance { get; } = new DestAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.DestAtop(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.DestAtop(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class DestOver : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static DestOver Instance { get; } = new DestOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.DestOver(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.DestOver(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class DestIn : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static DestIn Instance { get; } = new DestIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.DestIn(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.DestIn(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class DestOut : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static DestOut Instance { get; } = new DestOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.DestOut(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.DestOut(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Clear : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Clear Instance { get; } = new Clear(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Clear(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Clear(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Xor : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Xor Instance { get; } = new Xor(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Xor(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Xor(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt new file mode 100644 index 000000000..230b05499 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -0,0 +1,118 @@ +<# +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +// +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.Memory; + + + /// + /// Collection of Porter Duff alpha blending functions applying different composition models. + /// + /// + /// 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 DefaultPixelBlenders + where TPixel : struct, IPixel + { + +<# + + + + string[] blenders = new []{ + "Normal", + "Multiply", + "Add", + "Substract", + "Screen", + "Darken", + "Lighten", + "Overlay", + "HardLight", + "Src" , + "Atop" , + "Over" , + "In" , + "Out" , + "Dest" , + "DestAtop" , + "DestOver" , + "DestIn" , + "DestOut" , + "Clear" , + "Xor" , + }; + + + + foreach(var blender in blenders) { +#> + internal class <#=blender#> : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static <#=blender#> Instance { get; } = new <#=blender#>(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.<#=blender#>(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + 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 buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.<#=blender#>(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } +<# + + } + +#> + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs deleted file mode 100644 index 8405c3946..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Screen" blending to pixels. - /// - /// The type of the pixel - internal class DefaultScreenPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultScreenPixelBlender Instance { get; } = new DefaultScreenPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.ScreenFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - 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 buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.ScreenFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs deleted file mode 100644 index ab44cb760..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Subtract" blending to pixels. - /// - /// The type of the pixel - internal class DefaultSubstractPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultSubstractPixelBlender Instance { get; } = new DefaultSubstractPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.SubstractFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - 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 buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.SubstractFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs new file mode 100644 index 000000000..5bd4c601c --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -0,0 +1,314 @@ +// +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + + internal static partial class PorterDuffFunctions + { + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Src(Vector4 backdrop, Vector4 source, float amount) + { + source.W *= amount; + if (source.W == 0) + { + return Vector4.Zero; + } + + return Compose(Vector4.Zero, source, source); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Atop(Vector4 backdrop, Vector4 source, float amount) + { + return backdrop; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Over(Vector4 backdrop, Vector4 source, float amount) + { + source.W *= amount; + if (source.W == 0) + { + return backdrop; + } + + return Compose(backdrop, source, source); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 In(Vector4 backdrop, Vector4 source, float amount) + { + return Vector4.Zero; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Out(Vector4 backdrop, Vector4 source, float amount) + { + source.W *= amount; + if (source.W == 0) + { + return Vector4.Zero; + } + + return Compose(Vector4.Zero, source, Vector4.Zero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Dest(Vector4 backdrop, Vector4 source, float amount) + { + return backdrop; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DestAtop(Vector4 backdrop, Vector4 source, float amount) + { + source.W *= amount; + if (source.W == 0) + { + return Vector4.Zero; + } + + return Compose(Vector4.Zero, source, backdrop); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DestOver(Vector4 backdrop, Vector4 source, float amount) + { + source.W *= amount; + if (source.W == 0) + { + return backdrop; + } + + return Compose(backdrop, source, backdrop); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DestIn(Vector4 backdrop, Vector4 source, float amount) + { + return Vector4.Zero; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DestOut(Vector4 backdrop, Vector4 source, float amount) + { + return backdrop; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Clear(Vector4 backdrop, Vector4 source, float amount) + { + return Vector4.Zero; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Xor(Vector4 backdrop, Vector4 source, float amount) + { + source.W *= amount; + if (source.W == 0) + { + return backdrop; + } + + return Compose(backdrop, source, Vector4.Zero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Normal(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Normal(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Multiply(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Multiply(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Add(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Add(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Substract(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Substract(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Screen(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Screen(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Darken(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Darken(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Lighten(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Lighten(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Overlay(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Overlay(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLight(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(HardLight(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Src(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Src(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Atop(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Atop(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Over(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Over(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel In(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(In(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Out(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Out(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Dest(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Dest(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DestAtop(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(DestAtop(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DestOver(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(DestOver(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DestIn(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(DestIn(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DestOut(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(DestOut(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Clear(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Clear(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Xor(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Xor(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt new file mode 100644 index 000000000..15b2c46a3 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -0,0 +1,126 @@ +<# +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +// +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + + internal static partial class PorterDuffFunctions + { + +<# + + void GeneratePixelBlender (string blender) + { +#> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel <#=blender#>(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(<#=blender#>(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + +<# + } + + void GenerateVectorCompositor(string name, string sourceVar, string destVar, string blendVar) + { + if(sourceVar == "0") sourceVar= "Vector4.Zero"; + if(destVar == "0") destVar= "Vector4.Zero"; + if(blendVar == "0") blendVar= "Vector4.Zero"; + + if(sourceVar == "s") sourceVar= "source"; + if(destVar == "s") destVar= "source"; + if(blendVar == "s") blendVar= "source"; + + if(sourceVar == "d") sourceVar= "backdrop"; + if(destVar == "d") destVar= "backdrop"; + if(blendVar == "d") blendVar= "backdrop"; +#> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 <#=name#>(Vector4 backdrop, Vector4 source, float amount) + { +<# + if(sourceVar == "Vector4.Zero") + { +#> + return <#=destVar#>; +<# + }else{ +#> + <#=sourceVar#>.W *= amount; + if (<#=sourceVar#>.W == 0) + { + return <#=destVar#>; + } + + return Compose(<#=destVar#>, <#=sourceVar#>, <#=blendVar#>); +<# + } +#> + } + +<# + } + + + GenerateVectorCompositor("Src", "s", "0", "s"); + GenerateVectorCompositor("Atop", "0", "d", "s"); + GenerateVectorCompositor("Over", "s", "d", "s"); + GenerateVectorCompositor("In", "0", "0", "s"); + GenerateVectorCompositor("Out", "s", "0", "0"); + GenerateVectorCompositor("Dest", "0", "d", "d"); + GenerateVectorCompositor("DestAtop", "s", "0", "d"); + GenerateVectorCompositor("DestOver", "s", "d", "d"); + GenerateVectorCompositor("DestIn", "0", "0", "d"); + GenerateVectorCompositor("DestOut", "0", "d", "0"); + GenerateVectorCompositor("Clear", "0", "0", "0"); + GenerateVectorCompositor("Xor", "s", "d", "0"); + + + GeneratePixelBlender("Normal"); + GeneratePixelBlender("Multiply"); + GeneratePixelBlender("Add"); + GeneratePixelBlender("Substract"); + GeneratePixelBlender("Screen"); + GeneratePixelBlender("Darken"); + GeneratePixelBlender("Lighten"); + GeneratePixelBlender("Overlay"); + GeneratePixelBlender("HardLight"); + + GeneratePixelBlender("Src"); + GeneratePixelBlender("Atop"); + GeneratePixelBlender("Over"); + GeneratePixelBlender("In"); + GeneratePixelBlender("Out"); + GeneratePixelBlender("Dest"); + GeneratePixelBlender("DestAtop"); + GeneratePixelBlender("DestOver"); + GeneratePixelBlender("DestIn"); + GeneratePixelBlender("DestOut"); + GeneratePixelBlender("Clear"); + GeneratePixelBlender("Xor"); + + +#> + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index 25eb6a5c8..9e21d3510 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -29,7 +29,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 NormalBlendFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Normal(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; if (source.W == 0) @@ -48,7 +48,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 MultiplyFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Multiply(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; if (source.W == 0) @@ -67,7 +67,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 AddFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Add(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; if (source.W == 0) @@ -86,7 +86,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 SubstractFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Substract(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; if (source.W == 0) @@ -105,7 +105,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 ScreenFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Screen(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; if (source.W == 0) @@ -124,7 +124,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 DarkenFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Darken(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; if (source.W == 0) @@ -143,7 +143,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 LightenFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Lighten(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; if (source.W == 0) @@ -162,7 +162,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 OverlayFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Overlay(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; if (source.W == 0) @@ -185,7 +185,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 HardLightFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 HardLight(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; if (source.W == 0) @@ -222,7 +222,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector4 Compose(Vector4 backdrop, Vector4 source, Vector4 xform) { - DebugGuard.MustBeGreaterThan(source.W, 0, nameof(source.W)); + //DebugGuard.MustBeGreaterThan(source.W, 0, nameof(source.W)); // calculate weights float xw = backdrop.W * source.W; diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions{TPixel}.cs deleted file mode 100644 index 4e829212e..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions{TPixel}.cs +++ /dev/null @@ -1,151 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - /// - /// Collection of Porter Duff alpha blending functions - /// - /// 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 TPixel : IPixel - { - /// - /// Source over backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalBlendFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.NormalBlendFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Source multiplied by backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplyFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.MultiplyFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Source added to backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.AddFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Source substracted from backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubstractFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.SubstractFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Complement of source multiplied by the complement of backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.ScreenFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Per element, chooses the smallest value of source and backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.DarkenFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Per element, chooses the largest value of source and backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.LightenFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Overlays source over backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlayFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.OverlayFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Hard light effect - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [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; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs index cab357c41..5a3737dc6 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs @@ -22,25 +22,30 @@ namespace ImageSharp.PixelFormats { switch (mode) { - case PixelBlenderMode.Multiply: - return DefaultMultiplyPixelBlender.Instance; - case PixelBlenderMode.Add: - return DefaultAddPixelBlender.Instance; - case PixelBlenderMode.Substract: - return DefaultSubstractPixelBlender.Instance; - case PixelBlenderMode.Screen: - return DefaultScreenPixelBlender.Instance; - case PixelBlenderMode.Darken: - return DefaultDarkenPixelBlender.Instance; - case PixelBlenderMode.Lighten: - return DefaultLightenPixelBlender.Instance; - case PixelBlenderMode.Overlay: - return DefaultOverlayPixelBlender.Instance; - case PixelBlenderMode.HardLight: - return DefaultHardLightPixelBlender.Instance; + case PixelBlenderMode.Multiply: return DefaultPixelBlenders.Multiply.Instance; + case PixelBlenderMode.Add: return DefaultPixelBlenders.Add.Instance; + case PixelBlenderMode.Substract: return DefaultPixelBlenders.Substract.Instance; + case PixelBlenderMode.Screen: return DefaultPixelBlenders.Screen.Instance; + case PixelBlenderMode.Darken: return DefaultPixelBlenders.Darken.Instance; + case PixelBlenderMode.Lighten: return DefaultPixelBlenders.Lighten.Instance; + case PixelBlenderMode.Overlay: return DefaultPixelBlenders.Overlay.Instance; + case PixelBlenderMode.HardLight: return DefaultPixelBlenders.HardLight.Instance; + case PixelBlenderMode.Src: return DefaultPixelBlenders.Src.Instance; + case PixelBlenderMode.Atop: return DefaultPixelBlenders.Atop.Instance; + case PixelBlenderMode.Over: return DefaultPixelBlenders.Over.Instance; + case PixelBlenderMode.In: return DefaultPixelBlenders.In.Instance; + case PixelBlenderMode.Out: return DefaultPixelBlenders.Out.Instance; + case PixelBlenderMode.Dest: return DefaultPixelBlenders.Dest.Instance; + case PixelBlenderMode.DestAtop: return DefaultPixelBlenders.DestAtop.Instance; + case PixelBlenderMode.DestOver: return DefaultPixelBlenders.DestOver.Instance; + case PixelBlenderMode.DestIn: return DefaultPixelBlenders.DestIn.Instance; + case PixelBlenderMode.DestOut: return DefaultPixelBlenders.DestOut.Instance; + case PixelBlenderMode.Clear: return DefaultPixelBlenders.Clear.Instance; + case PixelBlenderMode.Xor: return DefaultPixelBlenders.Xor.Instance; + case PixelBlenderMode.Normal: default: - return DefaultNormalPixelBlender.Instance; + return DefaultPixelBlenders.Normal.Instance; } } } diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs index 1da69f1a8..7d7453ae3 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -35,7 +35,7 @@ namespace ImageSharp.Benchmarks for (int i = 0; i < destination.Length; i++) { - destinationSpan[i] = PorterDuffFunctions.NormalBlendFunction(backgroundSpan[i], sourceSpan[i], amount[i]); + destinationSpan[i] = PorterDuffFunctions.Normal(backgroundSpan[i], sourceSpan[i], amount[i]); } PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); @@ -50,7 +50,7 @@ namespace ImageSharp.Benchmarks for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalBlendFunction(destination[i], source[i], amount[i]); + destination[i] = PorterDuffFunctions.Normal(destination[i], source[i], amount[i]); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs index 45962c589..97d550592 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs @@ -24,7 +24,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(NormalBlendFunctionData))] public void NormalBlendFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.NormalBlendFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Normal((Vector4)back, source, amount); Assert.Equal(expected, actual); } @@ -43,7 +43,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(MultiplyFunctionData))] public void MultiplyFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.MultiplyFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Multiply((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -62,7 +62,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(AddFunctionData))] public void AddFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.MultiplyFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Multiply((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -81,7 +81,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(SubstractFunctionData))] public void SubstractFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.SubstractFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Substract((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -100,7 +100,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(ScreenFunctionData))] public void ScreenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.ScreenFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Screen((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -119,7 +119,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(DarkenFunctionData))] public void DarkenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.DarkenFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Darken((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -138,7 +138,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(LightenFunctionData))] public void LightenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.LightenFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Lighten((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -157,7 +157,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(OverlayFunctionData))] public void OverlayFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.OverlayFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Overlay((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -176,7 +176,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(HardLightFunctionData))] public void HardLightFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.HardLightFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.HardLight((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs index 8932f1ffe..b2a663d07 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -32,7 +32,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void NormalBlendFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.NormalBlendFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Normal((TPixel)(TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -41,7 +41,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void NormalBlendFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultNormalPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Normal().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -51,7 +51,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultNormalPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Normal().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -71,7 +71,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void MultiplyFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.MultiplyFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Multiply((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -80,7 +80,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void MultiplyFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultMultiplyPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Multiply().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -90,7 +90,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultMultiplyPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Multiply().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -110,7 +110,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void AddFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.AddFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Add((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -119,7 +119,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void AddFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultAddPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Add().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -129,7 +129,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultAddPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Add().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -149,7 +149,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void SubstractFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.SubstractFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Substract((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -158,7 +158,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void SubstractFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultSubstractPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Substract().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -168,7 +168,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultSubstractPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Substract().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -188,7 +188,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void ScreenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.ScreenFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Screen((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -197,7 +197,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void ScreenFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultScreenPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Screen().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -207,7 +207,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultScreenPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Screen().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -227,7 +227,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void DarkenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.DarkenFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Darken((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -236,7 +236,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void DarkenFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultDarkenPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Darken().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -246,7 +246,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultDarkenPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Darken().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -266,7 +266,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void LightenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.LightenFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Lighten((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -275,7 +275,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void LightenFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultLightenPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Lighten().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -285,7 +285,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultLightenPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Lighten().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -305,7 +305,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void OverlayFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.OverlayFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Overlay((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -314,7 +314,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void OverlayFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultOverlayPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Overlay().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -324,7 +324,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultOverlayPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Overlay().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -344,7 +344,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void HardLightFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.HardLightFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.HardLight((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -353,7 +353,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void HardLightFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultHardLightPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.HardLight().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -363,7 +363,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultHardLightPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.HardLight().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs index ce81499ed..6bcada0ff 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs @@ -15,27 +15,55 @@ namespace ImageSharp.Tests.PixelFormats public partial class PixelOperationsTests { + + public static TheoryData BlenderMappings = new TheoryData() { - { new TestPixel(), typeof(DefaultNormalPixelBlender), PixelBlenderMode.Normal }, - { new TestPixel(), typeof(DefaultScreenPixelBlender), PixelBlenderMode.Screen }, - { new TestPixel(), typeof(DefaultHardLightPixelBlender), PixelBlenderMode.HardLight }, - { new TestPixel(), typeof(DefaultOverlayPixelBlender), PixelBlenderMode.Overlay }, - { new TestPixel(), typeof(DefaultDarkenPixelBlender), PixelBlenderMode.Darken }, - { new TestPixel(), typeof(DefaultLightenPixelBlender), PixelBlenderMode.Lighten }, - { new TestPixel(), typeof(DefaultAddPixelBlender), PixelBlenderMode.Add }, - { new TestPixel(), typeof(DefaultSubstractPixelBlender), PixelBlenderMode.Substract }, - { new TestPixel(), typeof(DefaultMultiplyPixelBlender), PixelBlenderMode.Multiply }, - - { new TestPixel(), typeof(DefaultNormalPixelBlender), PixelBlenderMode.Normal }, - { new TestPixel(), typeof(DefaultScreenPixelBlender), PixelBlenderMode.Screen }, - { new TestPixel(), typeof(DefaultHardLightPixelBlender), PixelBlenderMode.HardLight }, - { new TestPixel(), typeof(DefaultOverlayPixelBlender), PixelBlenderMode.Overlay }, - { new TestPixel(), typeof(DefaultDarkenPixelBlender), PixelBlenderMode.Darken }, - { new TestPixel(), typeof(DefaultLightenPixelBlender), PixelBlenderMode.Lighten }, - { new TestPixel(), typeof(DefaultAddPixelBlender), PixelBlenderMode.Add }, - { new TestPixel(), typeof(DefaultSubstractPixelBlender), PixelBlenderMode.Substract }, - { new TestPixel(), typeof(DefaultMultiplyPixelBlender), PixelBlenderMode.Multiply }, + { new TestPixel(), typeof(DefaultPixelBlenders.Normal), PixelBlenderMode.Normal }, + { new TestPixel(), typeof(DefaultPixelBlenders.Screen), PixelBlenderMode.Screen }, + { new TestPixel(), typeof(DefaultPixelBlenders.HardLight), PixelBlenderMode.HardLight }, + { new TestPixel(), typeof(DefaultPixelBlenders.Overlay), PixelBlenderMode.Overlay }, + { new TestPixel(), typeof(DefaultPixelBlenders.Darken), PixelBlenderMode.Darken }, + { new TestPixel(), typeof(DefaultPixelBlenders.Lighten), PixelBlenderMode.Lighten }, + { new TestPixel(), typeof(DefaultPixelBlenders.Add), PixelBlenderMode.Add }, + { new TestPixel(), typeof(DefaultPixelBlenders.Substract), PixelBlenderMode.Substract }, + { new TestPixel(), typeof(DefaultPixelBlenders.Multiply), PixelBlenderMode.Multiply }, + + { new TestPixel(), typeof(DefaultPixelBlenders.Src), PixelBlenderMode.Src }, + { new TestPixel(), typeof(DefaultPixelBlenders.Atop), PixelBlenderMode.Atop }, + { new TestPixel(), typeof(DefaultPixelBlenders.Over), PixelBlenderMode.Over }, + { new TestPixel(), typeof(DefaultPixelBlenders.In), PixelBlenderMode.In }, + { new TestPixel(), typeof(DefaultPixelBlenders.Out), PixelBlenderMode.Out }, + { new TestPixel(), typeof(DefaultPixelBlenders.Dest), PixelBlenderMode.Dest }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestAtop), PixelBlenderMode.DestAtop }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestOver), PixelBlenderMode.DestOver }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestIn), PixelBlenderMode.DestIn }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestOut), PixelBlenderMode.DestOut }, + { new TestPixel(), typeof(DefaultPixelBlenders.Clear), PixelBlenderMode.Clear }, + { new TestPixel(), typeof(DefaultPixelBlenders.Xor), PixelBlenderMode.Xor }, + + { new TestPixel(), typeof(DefaultPixelBlenders.Normal), PixelBlenderMode.Normal }, + { new TestPixel(), typeof(DefaultPixelBlenders.Screen), PixelBlenderMode.Screen }, + { new TestPixel(), typeof(DefaultPixelBlenders.HardLight), PixelBlenderMode.HardLight }, + { new TestPixel(), typeof(DefaultPixelBlenders.Overlay), PixelBlenderMode.Overlay }, + { new TestPixel(), typeof(DefaultPixelBlenders.Darken), PixelBlenderMode.Darken }, + { new TestPixel(), typeof(DefaultPixelBlenders.Lighten), PixelBlenderMode.Lighten }, + { new TestPixel(), typeof(DefaultPixelBlenders.Add), PixelBlenderMode.Add }, + { new TestPixel(), typeof(DefaultPixelBlenders.Substract), PixelBlenderMode.Substract }, + { new TestPixel(), typeof(DefaultPixelBlenders.Multiply), PixelBlenderMode.Multiply }, + { new TestPixel(), typeof(DefaultPixelBlenders.Src), PixelBlenderMode.Src }, + { new TestPixel(), typeof(DefaultPixelBlenders.Atop), PixelBlenderMode.Atop }, + { new TestPixel(), typeof(DefaultPixelBlenders.Over), PixelBlenderMode.Over }, + { new TestPixel(), typeof(DefaultPixelBlenders.In), PixelBlenderMode.In }, + { new TestPixel(), typeof(DefaultPixelBlenders.Out), PixelBlenderMode.Out }, + { new TestPixel(), typeof(DefaultPixelBlenders.Dest), PixelBlenderMode.Dest }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestAtop), PixelBlenderMode.DestAtop }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestOver), PixelBlenderMode.DestOver }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestIn), PixelBlenderMode.DestIn }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestOut), PixelBlenderMode.DestOut }, + { new TestPixel(), typeof(DefaultPixelBlenders.Clear), PixelBlenderMode.Clear }, + { new TestPixel(), typeof(DefaultPixelBlenders.Xor), PixelBlenderMode.Xor }, + }; [Theory] From 69501ee20ce3bc59589d62145ecd0c6efde2dbda Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 17 Jun 2017 20:31:33 +0100 Subject: [PATCH 2/5] cleanup and reduce branches --- .../PorterDuffFunctions.Generated.cs | 80 ++++++------------- .../PorterDuffFunctions.Generated.tt | 52 ++++-------- .../PixelBlenders/PorterDuffFunctions.cs | 47 ----------- .../ImageSharp.Tests/Drawing/BlendedShapes.cs | 28 ++++++- 4 files changed, 68 insertions(+), 139 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index 5bd4c601c..a0e4495b6 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -14,110 +14,80 @@ namespace ImageSharp.PixelFormats.PixelBlenders { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Src(Vector4 backdrop, Vector4 source, float amount) - { - source.W *= amount; - if (source.W == 0) - { - return Vector4.Zero; - } - + public static Vector4 Src(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; return Compose(Vector4.Zero, source, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Atop(Vector4 backdrop, Vector4 source, float amount) + public static Vector4 Atop(Vector4 backdrop, Vector4 source, float opacity) { - return backdrop; + return Compose(backdrop, Vector4.Zero, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Over(Vector4 backdrop, Vector4 source, float amount) + public static Vector4 Over(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= amount; - if (source.W == 0) - { - return backdrop; - } - + source.W *= opacity; return Compose(backdrop, source, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 In(Vector4 backdrop, Vector4 source, float amount) + public static Vector4 In(Vector4 backdrop, Vector4 source, float opacity) { - return Vector4.Zero; + return Compose(Vector4.Zero, Vector4.Zero, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Out(Vector4 backdrop, Vector4 source, float amount) + public static Vector4 Out(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= amount; - if (source.W == 0) - { - return Vector4.Zero; - } - + source.W *= opacity; return Compose(Vector4.Zero, source, Vector4.Zero); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Dest(Vector4 backdrop, Vector4 source, float amount) + public static Vector4 Dest(Vector4 backdrop, Vector4 source, float opacity) { - return backdrop; + return Compose(backdrop, Vector4.Zero, backdrop); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 DestAtop(Vector4 backdrop, Vector4 source, float amount) + public static Vector4 DestAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= amount; - if (source.W == 0) - { - return Vector4.Zero; - } - + source.W *= opacity; return Compose(Vector4.Zero, source, backdrop); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 DestOver(Vector4 backdrop, Vector4 source, float amount) + public static Vector4 DestOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= amount; - if (source.W == 0) - { - return backdrop; - } - + source.W *= opacity; return Compose(backdrop, source, backdrop); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 DestIn(Vector4 backdrop, Vector4 source, float amount) + public static Vector4 DestIn(Vector4 backdrop, Vector4 source, float opacity) { - return Vector4.Zero; + return Compose(Vector4.Zero, Vector4.Zero, backdrop); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 DestOut(Vector4 backdrop, Vector4 source, float amount) + public static Vector4 DestOut(Vector4 backdrop, Vector4 source, float opacity) { - return backdrop; + return Compose(backdrop, Vector4.Zero, Vector4.Zero); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Clear(Vector4 backdrop, Vector4 source, float amount) + public static Vector4 Clear(Vector4 backdrop, Vector4 source, float opacity) { - return Vector4.Zero; + return Compose(Vector4.Zero, Vector4.Zero, Vector4.Zero); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Xor(Vector4 backdrop, Vector4 source, float amount) + public static Vector4 Xor(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= amount; - if (source.W == 0) - { - return backdrop; - } - + source.W *= opacity; return Compose(backdrop, source, Vector4.Zero); } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index 15b2c46a3..68bd65041 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -44,57 +44,37 @@ namespace ImageSharp.PixelFormats.PixelBlenders void GenerateVectorCompositor(string name, string sourceVar, string destVar, string blendVar) { - if(sourceVar == "0") sourceVar= "Vector4.Zero"; - if(destVar == "0") destVar= "Vector4.Zero"; - if(blendVar == "0") blendVar= "Vector4.Zero"; - - if(sourceVar == "s") sourceVar= "source"; - if(destVar == "s") destVar= "source"; - if(blendVar == "s") blendVar= "source"; - - if(sourceVar == "d") sourceVar= "backdrop"; - if(destVar == "d") destVar= "backdrop"; - if(blendVar == "d") blendVar= "backdrop"; #> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 <#=name#>(Vector4 backdrop, Vector4 source, float amount) + public static Vector4 <#=name#>(Vector4 backdrop, Vector4 source, float opacity) { <# - if(sourceVar == "Vector4.Zero") + if(sourceVar != "Vector4.Zero") { #> - return <#=destVar#>; -<# - }else{ -#> - <#=sourceVar#>.W *= amount; - if (<#=sourceVar#>.W == 0) - { - return <#=destVar#>; - } - - return Compose(<#=destVar#>, <#=sourceVar#>, <#=blendVar#>); + <#=sourceVar#>.W *= opacity; <# } #> + return Compose(<#=destVar#>, <#=sourceVar#>, <#=blendVar#>); } <# } - GenerateVectorCompositor("Src", "s", "0", "s"); - GenerateVectorCompositor("Atop", "0", "d", "s"); - GenerateVectorCompositor("Over", "s", "d", "s"); - GenerateVectorCompositor("In", "0", "0", "s"); - GenerateVectorCompositor("Out", "s", "0", "0"); - GenerateVectorCompositor("Dest", "0", "d", "d"); - GenerateVectorCompositor("DestAtop", "s", "0", "d"); - GenerateVectorCompositor("DestOver", "s", "d", "d"); - GenerateVectorCompositor("DestIn", "0", "0", "d"); - GenerateVectorCompositor("DestOut", "0", "d", "0"); - GenerateVectorCompositor("Clear", "0", "0", "0"); - GenerateVectorCompositor("Xor", "s", "d", "0"); + GenerateVectorCompositor("Src", "source", "Vector4.Zero", "source"); + GenerateVectorCompositor("Atop", "Vector4.Zero", "backdrop", "source"); + GenerateVectorCompositor("Over", "source", "backdrop", "source"); + GenerateVectorCompositor("In", "Vector4.Zero", "Vector4.Zero", "source"); + GenerateVectorCompositor("Out", "source", "Vector4.Zero", "Vector4.Zero"); + GenerateVectorCompositor("Dest", "Vector4.Zero", "backdrop", "backdrop"); + GenerateVectorCompositor("DestAtop", "source", "Vector4.Zero", "backdrop"); + GenerateVectorCompositor("DestOver", "source", "backdrop", "backdrop"); + GenerateVectorCompositor("DestIn", "Vector4.Zero", "Vector4.Zero", "backdrop"); + GenerateVectorCompositor("DestOut", "Vector4.Zero", "backdrop", "Vector4.Zero"); + GenerateVectorCompositor("Clear", "Vector4.Zero", "Vector4.Zero", "Vector4.Zero"); + GenerateVectorCompositor("Xor", "source", "backdrop", "Vector4.Zero"); GeneratePixelBlender("Normal"); diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index 9e21d3510..bbb6ca4de 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -32,11 +32,6 @@ namespace ImageSharp.PixelFormats.PixelBlenders public static Vector4 Normal(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, source); } @@ -51,11 +46,6 @@ namespace ImageSharp.PixelFormats.PixelBlenders public static Vector4 Multiply(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, backdrop * source); } @@ -70,11 +60,6 @@ namespace ImageSharp.PixelFormats.PixelBlenders public static Vector4 Add(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.Min(Vector4.One, backdrop + source)); } @@ -89,11 +74,6 @@ namespace ImageSharp.PixelFormats.PixelBlenders public static Vector4 Substract(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.Max(Vector4.Zero, backdrop - source)); } @@ -108,11 +88,6 @@ namespace ImageSharp.PixelFormats.PixelBlenders public static Vector4 Screen(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source))); } @@ -127,11 +102,6 @@ namespace ImageSharp.PixelFormats.PixelBlenders public static Vector4 Darken(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.Min(backdrop, source)); } @@ -146,11 +116,6 @@ namespace ImageSharp.PixelFormats.PixelBlenders public static Vector4 Lighten(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.Max(backdrop, source)); } @@ -165,11 +130,6 @@ namespace ImageSharp.PixelFormats.PixelBlenders public static Vector4 Overlay(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - float cr = OverlayValueFunction(backdrop.X, source.X); float cg = OverlayValueFunction(backdrop.Y, source.Y); float cb = OverlayValueFunction(backdrop.Z, source.Z); @@ -188,11 +148,6 @@ namespace ImageSharp.PixelFormats.PixelBlenders public static Vector4 HardLight(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - float cr = OverlayValueFunction(source.X, backdrop.X); float cg = OverlayValueFunction(source.Y, backdrop.Y); float cb = OverlayValueFunction(source.Z, backdrop.Z); @@ -222,8 +177,6 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector4 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; diff --git a/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs index 23f0569e7..a3df1803d 100644 --- a/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs +++ b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs @@ -41,7 +41,7 @@ namespace ImageSharp.Tests.Drawing using (var img = provider.GetImage()) { img.Fill(NamedColors.DarkBlue, new Rectangle(0, 40, 100, 20)); - img.Fill(NamedColors.HotPink, new Rectangle(20, 0, 40, 100), new ImageSharp.GraphicsOptions(true) + img.Fill(NamedColors.HotPink, new Rectangle(20, 0, 30, 100), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode }); @@ -52,5 +52,31 @@ namespace ImageSharp.Tests.Drawing img.DebugSave(provider, new { mode }); } } + + + [Theory] + [WithBlankImages(nameof(modes), 100, 100, PixelTypes.Rgba32)] + public void DrawBlendedValues_transparent50Percent(TestImageProvider provider, PixelBlenderMode mode) + where TPixel : struct, IPixel + { + using (var img = provider.GetImage()) + { + img.Fill(NamedColors.DarkBlue, new Rectangle(0, 40, 100, 20)); + img.Fill(NamedColors.HotPink, new Rectangle(20, 0, 30, 100), new ImageSharp.GraphicsOptions(true) + { + BlenderMode = mode + }); + var c = NamedColors.Red.ToVector4(); + c.W *= 0.5f; + TPixel pixel = default(TPixel); + pixel.PackFromVector4(c); + + img.Fill(pixel, new Rectangle(40, 0, 20, 100), new ImageSharp.GraphicsOptions(true) + { + BlenderMode = mode + }); + img.DebugSave(provider, new { mode }); + } + } } } From b76c21e7eebcbb90ea06190064b27bea4743a14d Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 18 Jun 2017 10:48:31 +0100 Subject: [PATCH 3/5] fix blending to allow non rectangualar regions to antialiase Added a sample to resize and added rounded corners to an image --- .gitignore | 1 + ImageSharp.sln | 19 +++++- .../AvatarWithRoundedCorner.csproj | 12 ++++ samples/AvatarWithRoundedCorner/Program.cs | 63 +++++++++++++++++++ samples/AvatarWithRoundedCorner/fb.jpg | 3 + .../PorterDuffFunctions.Generated.cs | 36 +++++------ .../PorterDuffFunctions.Generated.tt | 15 ++--- .../PixelBlenders/PorterDuffFunctions.cs | 34 +++++----- .../ImageSharp.Tests/Drawing/BlendedShapes.cs | 54 ++++++++++++---- 9 files changed, 179 insertions(+), 58 deletions(-) create mode 100644 samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj create mode 100644 samples/AvatarWithRoundedCorner/Program.cs create mode 100644 samples/AvatarWithRoundedCorner/fb.jpg diff --git a/.gitignore b/.gitignore index fb8af2320..8034c8c89 100644 --- a/.gitignore +++ b/.gitignore @@ -217,3 +217,4 @@ artifacts/ #CodeCoverage **/CodeCoverage/* docs/ +/samples/AvatarWithRoundedCorner/output diff --git a/ImageSharp.sln b/ImageSharp.sln index 2e0cbd52e..35998e1aa 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26403.7 +VisualStudioVersion = 15.0.26430.6 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" ProjectSection(SolutionItems) = preProject @@ -45,6 +45,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "te EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{96188137-5FA6-4924-AB6E-4EFF79C6E0BB}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{7CC6D57E-B916-43B8-B315-A0BB92F260A2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvatarWithRoundedCorner", "samples\AvatarWithRoundedCorner\AvatarWithRoundedCorner.csproj", "{844FC582-4E78-4371-847D-EFD4D1103578}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -127,6 +131,18 @@ Global {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x64.Build.0 = Release|Any CPU {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x86.ActiveCfg = Release|Any CPU {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x86.Build.0 = Release|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|Any CPU.Build.0 = Debug|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x64.ActiveCfg = Debug|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x64.Build.0 = Debug|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x86.ActiveCfg = Debug|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x86.Build.0 = Debug|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Release|Any CPU.ActiveCfg = Release|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Release|Any CPU.Build.0 = Release|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x64.ActiveCfg = Release|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x64.Build.0 = Release|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.ActiveCfg = Release|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -139,5 +155,6 @@ Global {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {96188137-5FA6-4924-AB6E-4EFF79C6E0BB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} + {844FC582-4E78-4371-847D-EFD4D1103578} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2} EndGlobalSection EndGlobal diff --git a/samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj b/samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj new file mode 100644 index 000000000..e000aacf1 --- /dev/null +++ b/samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp1.1 + + + + + + + \ No newline at end of file diff --git a/samples/AvatarWithRoundedCorner/Program.cs b/samples/AvatarWithRoundedCorner/Program.cs new file mode 100644 index 000000000..56b30744c --- /dev/null +++ b/samples/AvatarWithRoundedCorner/Program.cs @@ -0,0 +1,63 @@ + + +namespace AvatarWithRoundedCorner +{ + using System; + using System.Numerics; + using ImageSharp; + using SixLabors.Shapes; + + class Program + { + static void Main(string[] args) + { + + using (var image = Image.Load("fb.jpg")) + { + image.Resize(new ImageSharp.Processing.ResizeOptions + { + Size = new ImageSharp.Size(200, 200), + Mode = ImageSharp.Processing.ResizeMode.Crop + }); + + ApplyRoundedCourners(image, 30); + System.IO.Directory.CreateDirectory("output"); + image.Save("output/fb.png"); + } + } + + public static void ApplyRoundedCourners(Image img, float cornerRadius) + { + var corners = BuildCorners(img.Width, img.Height, cornerRadius); + // now we have our corners time to draw them + img.Fill(Rgba32.Transparent, corners, new GraphicsOptions(true) + { + BlenderMode = ImageSharp.PixelFormats.PixelBlenderMode.Src // enforces that any part of this shape that has color is punched out of the background + }); + } + + public static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius) + { + // first create a square + var rect = new SixLabors.Shapes.Rectangle(-0.5f, -0.5f, cornerRadius, cornerRadius); + + // then cut out of the square a circle so we are left with a corner + var cornerToptLeft = rect.Clip(new SixLabors.Shapes.Ellipse(cornerRadius-0.5f, cornerRadius - 0.5f, cornerRadius)); + + // corner is now a corner shape positions top left + //lets make 3 more positioned correctly, we cando that by translating the orgional artound the center of the image + var center = new Vector2(imageWidth / 2, imageHeight / 2); + var angle = Math.PI / 2f; + + float rightPos = imageWidth - cornerToptLeft.Bounds.Width +1; + float bottomPos = imageHeight - cornerToptLeft.Bounds.Height + 1; + + // move it across the widthof the image - the width of the shape + var cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0); + var cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos); + var cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos); + + return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight); + } + } +} \ No newline at end of file diff --git a/samples/AvatarWithRoundedCorner/fb.jpg b/samples/AvatarWithRoundedCorner/fb.jpg new file mode 100644 index 000000000..7241890e2 --- /dev/null +++ b/samples/AvatarWithRoundedCorner/fb.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93bb4d6281dc1e845db57e836e0dca30b7a4062e81044efb27ad4d8b1a33130c +size 15787 diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index a0e4495b6..a2cbb0719 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -16,79 +16,79 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Src(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(Vector4.Zero, source, source); + source.W *= opacity; + return Compose(Vector4.Zero, source, source).Blend(backdrop, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Atop(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(backdrop, Vector4.Zero, source); + return Compose(backdrop, Vector4.Zero, source).Blend(backdrop, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Over(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(backdrop, source, source); + source.W *= opacity; + return Compose(backdrop, source, source).Blend(backdrop, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 In(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(Vector4.Zero, Vector4.Zero, source); + return Compose(Vector4.Zero, Vector4.Zero, source).Blend(backdrop, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Out(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(Vector4.Zero, source, Vector4.Zero); + source.W *= opacity; + return Compose(Vector4.Zero, source, Vector4.Zero).Blend(backdrop, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Dest(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(backdrop, Vector4.Zero, backdrop); + return Compose(backdrop, Vector4.Zero, backdrop).Blend(backdrop, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DestAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(Vector4.Zero, source, backdrop); + source.W *= opacity; + return Compose(Vector4.Zero, source, backdrop).Blend(backdrop, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DestOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(backdrop, source, backdrop); + source.W *= opacity; + return Compose(backdrop, source, backdrop).Blend(backdrop, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DestIn(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(Vector4.Zero, Vector4.Zero, backdrop); + return Compose(Vector4.Zero, Vector4.Zero, backdrop).Blend(backdrop, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DestOut(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(backdrop, Vector4.Zero, Vector4.Zero); + return Compose(backdrop, Vector4.Zero, Vector4.Zero).Blend(backdrop, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Clear(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(Vector4.Zero, Vector4.Zero, Vector4.Zero); + return Compose(Vector4.Zero, Vector4.Zero, Vector4.Zero).Blend(backdrop, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Xor(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(backdrop, source, Vector4.Zero); + source.W *= opacity; + return Compose(backdrop, source, Vector4.Zero).Blend(backdrop, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index 68bd65041..1b60481b6 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -48,15 +48,12 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 <#=name#>(Vector4 backdrop, Vector4 source, float opacity) { -<# - if(sourceVar != "Vector4.Zero") - { -#> - <#=sourceVar#>.W *= opacity; -<# - } -#> - return Compose(<#=destVar#>, <#=sourceVar#>, <#=blendVar#>); + <# if(sourceVar != "Vector4.Zero") { + #> + <#=sourceVar#>.W *= opacity; + <# + } #> + return Compose(<#=destVar#>, <#=sourceVar#>, <#=blendVar#>).Blend(backdrop, opacity); } <# diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index bbb6ca4de..bc7bb2d90 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -31,8 +31,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Normal(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(backdrop, source, source); + return Over(backdrop, source, opacity); } /// @@ -45,8 +44,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Multiply(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(backdrop, source, backdrop * source); + return Compose(backdrop, source, backdrop * source).Blend(backdrop, opacity); } /// @@ -59,8 +57,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Add(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(backdrop, source, Vector4.Min(Vector4.One, backdrop + source)); + return Compose(backdrop, source, Vector4.Min(Vector4.One, backdrop + source)).Blend(backdrop, opacity); } /// @@ -73,8 +70,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Substract(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(backdrop, source, Vector4.Max(Vector4.Zero, backdrop - source)); + return Compose(backdrop, source, Vector4.Max(Vector4.Zero, backdrop - source)).Blend(backdrop, opacity); } /// @@ -87,8 +83,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Screen(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(backdrop, source, Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source))); + return Compose(backdrop, source, Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source))).Blend(backdrop, opacity); } /// @@ -101,8 +96,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Darken(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(backdrop, source, Vector4.Min(backdrop, source)); + return Compose(backdrop, source, Vector4.Min(backdrop, source)).Blend(backdrop, opacity); } /// @@ -115,8 +109,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Lighten(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(backdrop, source, Vector4.Max(backdrop, source)); + return Compose(backdrop, source, Vector4.Max(backdrop, source)).Blend(backdrop, opacity); } /// @@ -134,7 +127,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders float cg = OverlayValueFunction(backdrop.Y, source.Y); float cb = OverlayValueFunction(backdrop.Z, source.Z); - return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); + return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))).Blend(backdrop, opacity); } /// @@ -147,12 +140,11 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLight(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; float cr = OverlayValueFunction(source.X, backdrop.X); float cg = OverlayValueFunction(source.Y, backdrop.Y); float cb = OverlayValueFunction(source.Z, backdrop.Z); - return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); + return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))).Blend(backdrop, opacity); } /// @@ -186,10 +178,16 @@ namespace ImageSharp.PixelFormats.PixelBlenders float a = xw + bw + sw; // calculate final value - xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / a; + xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); xform.W = a; return xform; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Blend(this Vector4 source, Vector4 backdrop, float opacity) + { + return Vector4.Lerp(backdrop, source, opacity); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs index a3df1803d..de10fd24f 100644 --- a/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs +++ b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs @@ -18,14 +18,16 @@ namespace ImageSharp.Tests.Drawing .Select(x=> new object[] { x }); [Theory] - [WithBlankImages(nameof(modes), 100, 100, PixelTypes.Rgba32)] + [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] public void DrawBlendedValues(TestImageProvider provider, PixelBlenderMode mode) where TPixel : struct, IPixel { using (var img = provider.GetImage()) { - img.Fill(NamedColors.DarkBlue, new Rectangle(0, 40, 100, 20)); - img.Fill(NamedColors.HotPink, new Rectangle(40, 0, 20, 100), new ImageSharp.GraphicsOptions(true) + var scaleX = (img.Width / 100); + var scaleY = (img.Height / 100); + img.Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)); + img.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode }); @@ -34,18 +36,20 @@ namespace ImageSharp.Tests.Drawing } [Theory] - [WithBlankImages(nameof(modes), 100, 100, PixelTypes.Rgba32)] + [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] public void DrawBlendedValues_transparent(TestImageProvider provider, PixelBlenderMode mode) where TPixel : struct, IPixel { using (var img = provider.GetImage()) { - img.Fill(NamedColors.DarkBlue, new Rectangle(0, 40, 100, 20)); - img.Fill(NamedColors.HotPink, new Rectangle(20, 0, 30, 100), new ImageSharp.GraphicsOptions(true) + var scaleX = (img.Width / 100); + var scaleY = (img.Height / 100); + img.Fill(NamedColors.DarkBlue, new Rectangle(0* scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)); + img.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode }); - img.Fill(NamedColors.Transparent, new Rectangle(40, 0, 20, 100), new ImageSharp.GraphicsOptions(true) + img.Fill(NamedColors.Transparent, new SixLabors.Shapes.Ellipse(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode }); @@ -53,16 +57,17 @@ namespace ImageSharp.Tests.Drawing } } - [Theory] - [WithBlankImages(nameof(modes), 100, 100, PixelTypes.Rgba32)] + [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] public void DrawBlendedValues_transparent50Percent(TestImageProvider provider, PixelBlenderMode mode) where TPixel : struct, IPixel { using (var img = provider.GetImage()) { - img.Fill(NamedColors.DarkBlue, new Rectangle(0, 40, 100, 20)); - img.Fill(NamedColors.HotPink, new Rectangle(20, 0, 30, 100), new ImageSharp.GraphicsOptions(true) + var scaleX = (img.Width / 100); + var scaleY = (img.Height / 100); + img.Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40, 100 * scaleX, 20* scaleY)); + img.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode }); @@ -71,7 +76,32 @@ namespace ImageSharp.Tests.Drawing TPixel pixel = default(TPixel); pixel.PackFromVector4(c); - img.Fill(pixel, new Rectangle(40, 0, 20, 100), new ImageSharp.GraphicsOptions(true) + img.Fill(pixel, new SixLabors.Shapes.Ellipse(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) + { + BlenderMode = mode + }); + img.DebugSave(provider, new { mode }); + } + } + + + + [Theory] + [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] + public void DrawBlendedValues_doldidEllips(TestImageProvider provider, PixelBlenderMode mode) + where TPixel : struct, IPixel + { + using (var img = provider.GetImage()) + { + var scaleX = (img.Width / 100); + var scaleY = (img.Height / 100); + img.Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40* scaleY, 100 * scaleX, 20 * scaleY)); + //img.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) + //{ + // BlenderMode = mode + //}); + + img.Fill(NamedColors.Black, new SixLabors.Shapes.Ellipse(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode }); From 53c67b23873e22d83f21fb237d41ea71fcf43879 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 18 Jun 2017 12:40:47 +0100 Subject: [PATCH 4/5] ensure older blenders function as before --- .../PorterDuffFunctions.Generated.cs | 205 ++++++++++++++++-- .../PorterDuffFunctions.Generated.tt | 27 ++- .../PixelBlenders/PorterDuffFunctions.cs | 32 +-- 3 files changed, 221 insertions(+), 43 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index a2cbb0719..4213be0ba 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -12,83 +12,250 @@ namespace ImageSharp.PixelFormats.PixelBlenders internal static partial class PorterDuffFunctions { - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Src(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(Vector4.Zero, source, source).Blend(backdrop, opacity); + opacity = opacity.Clamp(0, 1); + source.W *= opacity; + Vector4 xform = source; + + // calculate weights + float xw = Vector4.Zero.W * source.W; + float bw = Vector4.Zero.W - xw; + float sw = source.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Atop(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(backdrop, Vector4.Zero, source).Blend(backdrop, opacity); + opacity = opacity.Clamp(0, 1); + Vector4 xform = source; + + // calculate weights + float xw = backdrop.W * Vector4.Zero.W; + float bw = backdrop.W - xw; + float sw = Vector4.Zero.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (backdrop * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Over(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(backdrop, source, source).Blend(backdrop, opacity); + opacity = opacity.Clamp(0, 1); + source.W *= opacity; + Vector4 xform = source; + + // calculate weights + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; + float sw = source.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 In(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(Vector4.Zero, Vector4.Zero, source).Blend(backdrop, opacity); + opacity = opacity.Clamp(0, 1); + Vector4 xform = source; + + // calculate weights + float xw = Vector4.Zero.W * Vector4.Zero.W; + float bw = Vector4.Zero.W - xw; + float sw = Vector4.Zero.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (Vector4.Zero * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Out(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(Vector4.Zero, source, Vector4.Zero).Blend(backdrop, opacity); + opacity = opacity.Clamp(0, 1); + source.W *= opacity; + Vector4 xform = Vector4.Zero; + + // calculate weights + float xw = Vector4.Zero.W * source.W; + float bw = Vector4.Zero.W - xw; + float sw = source.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Dest(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(backdrop, Vector4.Zero, backdrop).Blend(backdrop, opacity); + opacity = opacity.Clamp(0, 1); + Vector4 xform = backdrop; + + // calculate weights + float xw = backdrop.W * Vector4.Zero.W; + float bw = backdrop.W - xw; + float sw = Vector4.Zero.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (backdrop * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DestAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(Vector4.Zero, source, backdrop).Blend(backdrop, opacity); + opacity = opacity.Clamp(0, 1); + source.W *= opacity; + Vector4 xform = backdrop; + + // calculate weights + float xw = Vector4.Zero.W * source.W; + float bw = Vector4.Zero.W - xw; + float sw = source.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DestOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(backdrop, source, backdrop).Blend(backdrop, opacity); + opacity = opacity.Clamp(0, 1); + source.W *= opacity; + Vector4 xform = backdrop; + + // calculate weights + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; + float sw = source.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DestIn(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(Vector4.Zero, Vector4.Zero, backdrop).Blend(backdrop, opacity); + opacity = opacity.Clamp(0, 1); + Vector4 xform = backdrop; + + // calculate weights + float xw = Vector4.Zero.W * Vector4.Zero.W; + float bw = Vector4.Zero.W - xw; + float sw = Vector4.Zero.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (Vector4.Zero * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DestOut(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(backdrop, Vector4.Zero, Vector4.Zero).Blend(backdrop, opacity); + opacity = opacity.Clamp(0, 1); + Vector4 xform = Vector4.Zero; + + // calculate weights + float xw = backdrop.W * Vector4.Zero.W; + float bw = backdrop.W - xw; + float sw = Vector4.Zero.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (backdrop * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Clear(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(Vector4.Zero, Vector4.Zero, Vector4.Zero).Blend(backdrop, opacity); + opacity = opacity.Clamp(0, 1); + Vector4 xform = Vector4.Zero; + + // calculate weights + float xw = Vector4.Zero.W * Vector4.Zero.W; + float bw = Vector4.Zero.W - xw; + float sw = Vector4.Zero.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (Vector4.Zero * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Xor(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; - return Compose(backdrop, source, Vector4.Zero).Blend(backdrop, opacity); + opacity = opacity.Clamp(0, 1); + source.W *= opacity; + Vector4 xform = Vector4.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)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index 1b60481b6..53d22d8f3 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -24,7 +24,6 @@ namespace ImageSharp.PixelFormats.PixelBlenders internal static partial class PorterDuffFunctions { - <# void GeneratePixelBlender (string blender) @@ -48,18 +47,28 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 <#=name#>(Vector4 backdrop, Vector4 source, float opacity) { - <# if(sourceVar != "Vector4.Zero") { - #> - <#=sourceVar#>.W *= opacity; - <# - } #> - return Compose(<#=destVar#>, <#=sourceVar#>, <#=blendVar#>).Blend(backdrop, opacity); + opacity = opacity.Clamp(0, 1); +<# if(sourceVar != "Vector4.Zero" ) { #> + source.W *= opacity; +<# } #> + Vector4 xform = <#=blendVar#>; + + // calculate weights + float xw = <#=destVar#>.W * <#=sourceVar#>.W; + float bw = <#=destVar#>.W - xw; + float sw = <#=sourceVar#>.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (<#=destVar#> * bw) + (<#=sourceVar#> * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); } <# } - - GenerateVectorCompositor("Src", "source", "Vector4.Zero", "source"); GenerateVectorCompositor("Atop", "Vector4.Zero", "backdrop", "source"); GenerateVectorCompositor("Over", "source", "backdrop", "source"); diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index bc7bb2d90..b1fca9520 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -31,7 +31,8 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Normal(Vector4 backdrop, Vector4 source, float opacity) { - return Over(backdrop, source, opacity); + source.W *= opacity; + return Compose(backdrop, source, source); } /// @@ -44,7 +45,8 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Multiply(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(backdrop, source, backdrop * source).Blend(backdrop, opacity); + source.W *= opacity; + return Compose(backdrop, source, backdrop * source); } /// @@ -57,7 +59,8 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Add(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(backdrop, source, Vector4.Min(Vector4.One, backdrop + source)).Blend(backdrop, opacity); + source.W *= opacity; + return Compose(backdrop, source, Vector4.Min(Vector4.One, backdrop + source)); } /// @@ -70,7 +73,8 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Substract(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(backdrop, source, Vector4.Max(Vector4.Zero, backdrop - source)).Blend(backdrop, opacity); + source.W *= opacity; + return Compose(backdrop, source, Vector4.Max(Vector4.Zero, backdrop - source)); } /// @@ -83,7 +87,8 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Screen(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(backdrop, source, Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source))).Blend(backdrop, opacity); + source.W *= opacity; + return Compose(backdrop, source, Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source))); } /// @@ -96,7 +101,8 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Darken(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(backdrop, source, Vector4.Min(backdrop, source)).Blend(backdrop, opacity); + source.W *= opacity; + return Compose(backdrop, source, Vector4.Min(backdrop, source)); } /// @@ -109,7 +115,8 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Lighten(Vector4 backdrop, Vector4 source, float opacity) { - return Compose(backdrop, source, Vector4.Max(backdrop, source)).Blend(backdrop, opacity); + source.W *= opacity; + return Compose(backdrop, source, Vector4.Max(backdrop, source)); } /// @@ -127,7 +134,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders float cg = OverlayValueFunction(backdrop.Y, source.Y); float cb = OverlayValueFunction(backdrop.Z, source.Z); - return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))).Blend(backdrop, opacity); + return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); } /// @@ -140,11 +147,12 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLight(Vector4 backdrop, Vector4 source, float opacity) { + source.W *= opacity; float cr = OverlayValueFunction(source.X, backdrop.X); float cg = OverlayValueFunction(source.Y, backdrop.Y); float cb = OverlayValueFunction(source.Z, backdrop.Z); - return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))).Blend(backdrop, opacity); + return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); } /// @@ -183,11 +191,5 @@ namespace ImageSharp.PixelFormats.PixelBlenders return xform; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector4 Blend(this Vector4 source, Vector4 backdrop, float opacity) - { - return Vector4.Lerp(backdrop, source, opacity); - } } } \ No newline at end of file From 87e43acdaadbf5cd8f184dcdc10a44ebce1f2b55 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 18 Jun 2017 12:48:16 +0100 Subject: [PATCH 5/5] impove sample --- samples/AvatarWithRoundedCorner/Program.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/samples/AvatarWithRoundedCorner/Program.cs b/samples/AvatarWithRoundedCorner/Program.cs index 56b30744c..5516a73fb 100644 --- a/samples/AvatarWithRoundedCorner/Program.cs +++ b/samples/AvatarWithRoundedCorner/Program.cs @@ -11,18 +11,25 @@ namespace AvatarWithRoundedCorner { static void Main(string[] args) { - - using (var image = Image.Load("fb.jpg")) + System.IO.Directory.CreateDirectory("output"); + + GenerateAvatar("fb.jpg", "output/fb.png", new ImageSharp.Size(200, 200), 20); + GenerateAvatar("fb.jpg", "output/fb-round.png", new ImageSharp.Size(200, 200), 100); + GenerateAvatar("fb.jpg", "output/fb-rounder.png", new ImageSharp.Size(200, 200), 150); + } + + private static void GenerateAvatar(string source, string destination, ImageSharp.Size size, float cornerRadius) + { + using (var image = Image.Load(source)) { image.Resize(new ImageSharp.Processing.ResizeOptions { - Size = new ImageSharp.Size(200, 200), + Size = size, Mode = ImageSharp.Processing.ResizeMode.Crop }); - ApplyRoundedCourners(image, 30); - System.IO.Directory.CreateDirectory("output"); - image.Save("output/fb.png"); + ApplyRoundedCourners(image, cornerRadius); + image.Save(destination); } }