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/.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..5516a73fb --- /dev/null +++ b/samples/AvatarWithRoundedCorner/Program.cs @@ -0,0 +1,70 @@ + + +namespace AvatarWithRoundedCorner +{ + using System; + using System.Numerics; + using ImageSharp; + using SixLabors.Shapes; + + class Program + { + static void Main(string[] args) + { + 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 = size, + Mode = ImageSharp.Processing.ResizeMode.Crop + }); + + ApplyRoundedCourners(image, cornerRadius); + image.Save(destination); + } + } + + 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/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..4213be0ba --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -0,0 +1,451 @@ +// +// +// 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 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) + { + 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) + { + 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) + { + 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) + { + 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) + { + 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) + { + 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) + { + 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) + { + 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) + { + 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) + { + 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) + { + 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)] + 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..53d22d8f3 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -0,0 +1,112 @@ +<# +// +// 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) + { +#> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 <#=name#>(Vector4 backdrop, Vector4 source, float 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"); + 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"); + 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..b1fca9520 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -29,14 +29,9 @@ 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) - { - return backdrop; - } - return Compose(backdrop, source, source); } @@ -48,14 +43,9 @@ 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) - { - return backdrop; - } - return Compose(backdrop, source, backdrop * source); } @@ -67,14 +57,9 @@ 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) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.Min(Vector4.One, backdrop + source)); } @@ -86,14 +71,9 @@ 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) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.Max(Vector4.Zero, backdrop - source)); } @@ -105,14 +85,9 @@ 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) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source))); } @@ -124,14 +99,9 @@ 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) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.Min(backdrop, source)); } @@ -143,14 +113,9 @@ 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) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.Max(backdrop, source)); } @@ -162,14 +127,9 @@ 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) - { - return backdrop; - } - float cr = OverlayValueFunction(backdrop.X, source.X); float cg = OverlayValueFunction(backdrop.Y, source.Y); float cb = OverlayValueFunction(backdrop.Z, source.Z); @@ -185,14 +145,9 @@ 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) - { - 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; @@ -233,7 +186,7 @@ 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; 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/Drawing/BlendedShapes.cs b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs index 23f0569e7..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,72 @@ 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, 40, 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 + }); + img.DebugSave(provider, new { mode }); + } + } + + [Theory] + [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] + public void DrawBlendedValues_transparent50Percent(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, 100 * scaleX, 20* scaleY)); + img.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY), 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 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 }); 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]