From fc06762f4e26032e9c87e3b085d826a37f2e22a3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 20 Mar 2018 13:43:05 +1100 Subject: [PATCH 1/5] Fix spelling --- .../DefaultPixelBlenders.Generated.cs | 8 +++---- .../DefaultPixelBlenders.Generated.tt | 2 +- .../PorterDuffFunctions.Generated.cs | 4 ++-- .../PorterDuffFunctions.Generated.tt | 2 +- .../PixelBlenders/PorterDuffFunctions.cs | 24 +++++++++---------- .../PixelBlenders/PorterDuffFunctionsTests.cs | 2 +- .../PorterDuffFunctionsTests_TPixel.cs | 2 +- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index d3c6cf16c..6635a5a2a 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -141,17 +141,17 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class Substract : PixelBlender + internal class Subtract : PixelBlender { /// /// Gets the static instance of this blender. /// - public static Substract Instance { get; } = new Substract(); + public static Subtract Instance { get; } = new Subtract(); /// public override TPixel Blend(TPixel background, TPixel source, float amount) { - return PorterDuffFunctions.Substract(background, source, amount); + return PorterDuffFunctions.Subtract(background, source, amount); } /// @@ -172,7 +172,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders for (int i = 0; i < destination.Length; i++) { - destinationSpan[i] = PorterDuffFunctions.Substract(backgroundSpan[i], sourceSpan[i], amount[i]); + destinationSpan[i] = PorterDuffFunctions.Subtract(backgroundSpan[i], sourceSpan[i], amount[i]); } PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index eebee676f..485bc31ad 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders "Normal", "Multiply", "Add", - "Substract", + "Subtract", "Screen", "Darken", "Lighten", diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index 21ae335be..43fcc68ad 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -285,11 +285,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel Substract(TPixel backdrop, TPixel source, float amount) + public static TPixel Subtract(TPixel backdrop, TPixel source, float amount) where TPixel : struct, IPixel { TPixel dest = default(TPixel); - dest.PackFromVector4(Substract(backdrop.ToVector4(), source.ToVector4(), amount)); + dest.PackFromVector4(Subtract(backdrop.ToVector4(), source.ToVector4(), amount)); return dest; } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index c92ab6dd6..f3e0b1f17 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders GeneratePixelBlender("Normal"); GeneratePixelBlender("Multiply"); GeneratePixelBlender("Add"); - GeneratePixelBlender("Substract"); + GeneratePixelBlender("Subtract"); GeneratePixelBlender("Screen"); GeneratePixelBlender("Darken"); GeneratePixelBlender("Lighten"); diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index f09d6d51c..c47ef35a3 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// /// Source over backdrop /// - /// Backgrop color + /// Backdrop color /// Source color /// Opacity applied to Source Alpha /// Output color @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// /// Source multiplied by backdrop /// - /// Backgrop color + /// Backdrop color /// Source color /// Opacity applied to Source Alpha /// Output color @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// /// Source added to backdrop /// - /// Backgrop color + /// Backdrop color /// Source color /// Opacity applied to Source Alpha /// Output color @@ -63,14 +63,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Source substracted from backdrop + /// Source subtracted from backdrop /// - /// Backgrop color + /// Backdrop color /// Source color /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Substract(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Subtract(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Compose(backdrop, source, Vector4.Max(Vector4.Zero, backdrop - source)); @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// /// Complement of source multiplied by the complement of backdrop /// - /// Backgrop color + /// Backdrop color /// Source color /// Opacity applied to Source Alpha /// Output color @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// /// Per element, chooses the smallest value of source and backdrop /// - /// Backgrop color + /// Backdrop color /// Source color /// Opacity applied to Source Alpha /// Output color @@ -107,7 +107,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// /// Per element, chooses the largest value of source and backdrop /// - /// Backgrop color + /// Backdrop color /// Source color /// Opacity applied to Source Alpha /// Output color @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// /// Overlays source over backdrop /// - /// Backgrop color + /// Backdrop color /// Source color /// Opacity applied to Source Alpha /// Output color @@ -139,7 +139,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// /// Hard light effect /// - /// Backgrop color + /// Backdrop color /// Source color /// Opacity applied to Source Alpha /// Output color @@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// /// General composition function for all modes, with a general solution for alpha channel /// - /// Original backgrop color + /// Original Backdrop color /// Original source color /// Desired transformed color, without taking Alpha channel in account /// The final color diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs index 9aa2e01a6..c5910e13a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(SubstractFunctionData))] public void SubstractFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.Substract((Vector4)back, source, amount); + Vector4 actual = PorterDuffFunctions.Subtract((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 50babde69..d77c42086 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void SubstractFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.Substract((TPixel)back, source, amount); + TPixel actual = PorterDuffFunctions.Subtract((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } From 4059448dc634b4cb58b0df86a7382988f3202394 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 20 Mar 2018 16:21:01 +1100 Subject: [PATCH 2/5] Add basic comparision tests --- .../PixelFormats/PixelBlenderMode.cs | 2 +- .../PixelOperations{TPixel}.PixelBenders.cs | 2 +- .../ImageSharp.Tests/Drawing/DrawImageTest.cs | 2 +- .../PorterDuffCompositorTests.cs | 52 +++++++++++ .../PorterDuffFunctionsTests_TPixel.cs | 4 +- .../PixelOperationsTests.Blender.cs | 88 +++++++++--------- tests/ImageSharp.Tests/TestImages.cs | 2 + tests/Images/Input/Png/pd-dest.png | Bin 0 -> 2563 bytes tests/Images/Input/Png/pd-source.png | Bin 0 -> 2393 bytes 9 files changed, 103 insertions(+), 49 deletions(-) create mode 100644 tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs create mode 100644 tests/Images/Input/Png/pd-dest.png create mode 100644 tests/Images/Input/Png/pd-source.png diff --git a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs index 7541be789..4b8f56d76 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Blends the 2 values by subtraction. /// - Substract, + Subtract, /// /// Multiplies the complements of the backdrop and source values, then complements the result. diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs index 154ec7373..2c225ba4c 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.PixelFormats { case PixelBlenderMode.Multiply: return DefaultPixelBlenders.Multiply.Instance; case PixelBlenderMode.Add: return DefaultPixelBlenders.Add.Instance; - case PixelBlenderMode.Substract: return DefaultPixelBlenders.Substract.Instance; + case PixelBlenderMode.Subtract: return DefaultPixelBlenders.Subtract.Instance; case PixelBlenderMode.Screen: return DefaultPixelBlenders.Screen.Instance; case PixelBlenderMode.Darken: return DefaultPixelBlenders.Darken.Instance; case PixelBlenderMode.Lighten: return DefaultPixelBlenders.Lighten.Instance; diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index 3e7f3648f..0ff0b8557 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Tests [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Normal)] [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Multiply)] [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Add)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Substract)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Subtract)] [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Screen)] [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Darken)] [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Lighten)] diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs new file mode 100644 index 000000000..90b963f5c --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Drawing; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders +{ + public class PorterDuffCompositorTests + { + // TODO: Add other modes to compare. + private static PixelBlenderMode[] CompositingOperators = + { + PixelBlenderMode.Src, + PixelBlenderMode.Atop, + PixelBlenderMode.Over, + PixelBlenderMode.In, + PixelBlenderMode.Out, + PixelBlenderMode.Dest, + PixelBlenderMode.DestAtop, + PixelBlenderMode.DestOver, + PixelBlenderMode.DestIn, + PixelBlenderMode.DestOut, + PixelBlenderMode.Clear, + PixelBlenderMode.Xor + }; + + [Fact] + public void PorterDuffOutputIsCorrect() + { + string path = TestEnvironment.CreateOutputDirectory("PorterDuff"); + var srcFile = TestFile.Create(TestImages.Png.PDSrc); + var destFile = TestFile.Create(TestImages.Png.PDDest); + + using (Image src = srcFile.CreateImage()) + using (Image dest = destFile.CreateImage()) + { + foreach (PixelBlenderMode m in CompositingOperators) + { + using (Image res = dest.Clone(x => x.Blend(src, new GraphicsOptions { BlenderMode = m }))) + { + // TODO: Generate reference files once this works. + res.Save($"{path}/{m}.png"); + } + } + } + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs index d77c42086..10a34ec31 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -160,7 +160,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void SubstractFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.Substract().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Subtract().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -170,7 +170,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.Substract().Blend(this.MemoryManager, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Subtract().Blend(this.MemoryManager, 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 524747afe..d3956ecd5 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs @@ -16,53 +16,53 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats public static TheoryData BlenderMappings = new TheoryData() - { - { 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.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.Subtract), PixelBlenderMode.Subtract }, + { 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.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 }, + { 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.Subtract), PixelBlenderMode.Subtract }, + { 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] [MemberData(nameof(BlenderMappings))] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index db469f87e..4e9c3192d 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -36,6 +36,8 @@ namespace SixLabors.ImageSharp.Tests public const string SnakeGame = "Png/SnakeGame.png"; public const string Icon = "Png/icon.png"; public const string Kaboom = "Png/kaboom.png"; + public const string PDSrc = "Png/pd-source.png"; + public const string PDDest = "Png/pd-dest.png"; // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html public const string Filter0 = "Png/filter0.png"; diff --git a/tests/Images/Input/Png/pd-dest.png b/tests/Images/Input/Png/pd-dest.png new file mode 100644 index 0000000000000000000000000000000000000000..8db8ce173d75e4e458a7c6c7005ac5a46d07f9f4 GIT binary patch literal 2563 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 Date: Tue, 20 Mar 2018 22:54:38 +0100 Subject: [PATCH 3/5] turning PorterDuffOutputIsCorrect() into a [Theory] --- .../PorterDuffCompositorTests.cs | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs index 90b963f5c..f70fa0a2a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs @@ -1,35 +1,37 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Drawing; - -using Xunit; - namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Processing; + using SixLabors.ImageSharp.Processing.Drawing; + + using Xunit; + public class PorterDuffCompositorTests { // TODO: Add other modes to compare. - private static PixelBlenderMode[] CompositingOperators = - { - PixelBlenderMode.Src, - PixelBlenderMode.Atop, - PixelBlenderMode.Over, - PixelBlenderMode.In, - PixelBlenderMode.Out, - PixelBlenderMode.Dest, - PixelBlenderMode.DestAtop, - PixelBlenderMode.DestOver, - PixelBlenderMode.DestIn, - PixelBlenderMode.DestOut, - PixelBlenderMode.Clear, - PixelBlenderMode.Xor - }; + public static readonly TheoryData CompositingOperators = + new TheoryData + { + PixelBlenderMode.Src, + PixelBlenderMode.Atop, + PixelBlenderMode.Over, + PixelBlenderMode.In, + PixelBlenderMode.Out, + PixelBlenderMode.Dest, + PixelBlenderMode.DestAtop, + PixelBlenderMode.DestOver, + PixelBlenderMode.DestIn, + PixelBlenderMode.DestOut, + PixelBlenderMode.Clear, + PixelBlenderMode.Xor + }; - [Fact] - public void PorterDuffOutputIsCorrect() + [Theory] + [MemberData(nameof(CompositingOperators))] + public void PorterDuffOutputIsCorrect(PixelBlenderMode mode) { string path = TestEnvironment.CreateOutputDirectory("PorterDuff"); var srcFile = TestFile.Create(TestImages.Png.PDSrc); @@ -38,15 +40,12 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders using (Image src = srcFile.CreateImage()) using (Image dest = destFile.CreateImage()) { - foreach (PixelBlenderMode m in CompositingOperators) + using (Image res = dest.Clone(x => x.Blend(src, new GraphicsOptions { BlenderMode = mode }))) { - using (Image res = dest.Clone(x => x.Blend(src, new GraphicsOptions { BlenderMode = m }))) - { - // TODO: Generate reference files once this works. - res.Save($"{path}/{m}.png"); - } + // TODO: Generate reference files once this works. + res.Save($"{path}/{mode}.png"); } } } } -} +} \ No newline at end of file From 64345bb677c25a2f46a4cefe36f8359e2767f756 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Wed, 21 Mar 2018 00:10:03 +0100 Subject: [PATCH 4/5] Corrected implementation of PorterDuffFunctions. --- .../PorterDuffFunctions.Generated.cs | 126 +++++++++--------- .../PorterDuffFunctions.Generated.tt | 17 ++- 2 files changed, 73 insertions(+), 70 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index 43fcc68ad..e948c05ca 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -15,19 +15,19 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public static Vector4 Src(Vector4 backdrop, Vector4 source, float opacity) { opacity = opacity.Clamp(0, 1); - source.W *= opacity; - Vector4 xform = source; + source.W *= opacity; // calculate weights - float xw = Vector4.Zero.W * source.W; - float bw = Vector4.Zero.W - xw; + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; float sw = source.W - xw; // calculate final alpha - float a = xw + bw + sw; + float fw = (sw * 1) + (bw * 0) + (xw * 1); // calculate final value - xform = ((xform * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + Vector4 xform = ((source * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(fw, Constants.Epsilon); + xform.W = fw; return Vector4.Lerp(backdrop, xform, opacity); } @@ -36,18 +36,18 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders 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 xw = backdrop.W * source.W; float bw = backdrop.W - xw; - float sw = Vector4.Zero.W - xw; + float sw = source.W - xw; // calculate final alpha - float a = xw + bw + sw; + float fw = (sw * 0) + (bw * 1) + (xw * 1); // calculate final value - xform = ((xform * xw) + (backdrop * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + Vector4 xform = ((source * xw) + (backdrop * bw) + (Vector4.Zero * sw)) / MathF.Max(fw, Constants.Epsilon); + xform.W = fw; return Vector4.Lerp(backdrop, xform, opacity); } @@ -56,8 +56,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public static Vector4 Over(Vector4 backdrop, Vector4 source, float opacity) { opacity = opacity.Clamp(0, 1); - source.W *= opacity; - Vector4 xform = source; + source.W *= opacity; // calculate weights float xw = backdrop.W * source.W; @@ -65,10 +64,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders float sw = source.W - xw; // calculate final alpha - float a = xw + bw + sw; + float fw = (sw * 1) + (bw * 1) + (xw * 1); // calculate final value - xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + Vector4 xform = ((source * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(fw, Constants.Epsilon); + xform.W = fw; return Vector4.Lerp(backdrop, xform, opacity); } @@ -77,18 +77,18 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders 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; + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; + float sw = source.W - xw; // calculate final alpha - float a = xw + bw + sw; + float fw = (sw * 0) + (bw * 0) + (xw * 1); // calculate final value - xform = ((xform * xw) + (Vector4.Zero * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + Vector4 xform = ((source * xw) + (Vector4.Zero * bw) + (Vector4.Zero * sw)) / MathF.Max(fw, Constants.Epsilon); + xform.W = fw; return Vector4.Lerp(backdrop, xform, opacity); } @@ -97,19 +97,19 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public static Vector4 Out(Vector4 backdrop, Vector4 source, float opacity) { opacity = opacity.Clamp(0, 1); - source.W *= opacity; - Vector4 xform = Vector4.Zero; + source.W *= opacity; // calculate weights - float xw = Vector4.Zero.W * source.W; - float bw = Vector4.Zero.W - xw; + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; float sw = source.W - xw; // calculate final alpha - float a = xw + bw + sw; + float fw = (sw * 1) + (bw * 0) + (xw * 0); // calculate final value - xform = ((xform * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + Vector4 xform = ((Vector4.Zero * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(fw, Constants.Epsilon); + xform.W = fw; return Vector4.Lerp(backdrop, xform, opacity); } @@ -118,18 +118,18 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders 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 xw = backdrop.W * source.W; float bw = backdrop.W - xw; - float sw = Vector4.Zero.W - xw; + float sw = source.W - xw; // calculate final alpha - float a = xw + bw + sw; + float fw = (sw * 0) + (bw * 1) + (xw * 1); // calculate final value - xform = ((xform * xw) + (backdrop * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + Vector4 xform = ((backdrop * xw) + (backdrop * bw) + (Vector4.Zero * sw)) / MathF.Max(fw, Constants.Epsilon); + xform.W = fw; return Vector4.Lerp(backdrop, xform, opacity); } @@ -138,19 +138,19 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public static Vector4 DestAtop(Vector4 backdrop, Vector4 source, float opacity) { opacity = opacity.Clamp(0, 1); - source.W *= opacity; - Vector4 xform = backdrop; + source.W *= opacity; // calculate weights - float xw = Vector4.Zero.W * source.W; - float bw = Vector4.Zero.W - xw; + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; float sw = source.W - xw; // calculate final alpha - float a = xw + bw + sw; + float fw = (sw * 1) + (bw * 0) + (xw * 1); // calculate final value - xform = ((xform * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + Vector4 xform = ((backdrop * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(fw, Constants.Epsilon); + xform.W = fw; return Vector4.Lerp(backdrop, xform, opacity); } @@ -159,8 +159,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public static Vector4 DestOver(Vector4 backdrop, Vector4 source, float opacity) { opacity = opacity.Clamp(0, 1); - source.W *= opacity; - Vector4 xform = backdrop; + source.W *= opacity; // calculate weights float xw = backdrop.W * source.W; @@ -168,10 +167,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders float sw = source.W - xw; // calculate final alpha - float a = xw + bw + sw; + float fw = (sw * 1) + (bw * 1) + (xw * 1); // calculate final value - xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + Vector4 xform = ((backdrop * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(fw, Constants.Epsilon); + xform.W = fw; return Vector4.Lerp(backdrop, xform, opacity); } @@ -180,18 +180,18 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders 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; + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; + float sw = source.W - xw; // calculate final alpha - float a = xw + bw + sw; + float fw = (sw * 0) + (bw * 0) + (xw * 1); // calculate final value - xform = ((xform * xw) + (Vector4.Zero * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + Vector4 xform = ((backdrop * xw) + (Vector4.Zero * bw) + (Vector4.Zero * sw)) / MathF.Max(fw, Constants.Epsilon); + xform.W = fw; return Vector4.Lerp(backdrop, xform, opacity); } @@ -200,18 +200,18 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders 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 xw = backdrop.W * source.W; float bw = backdrop.W - xw; - float sw = Vector4.Zero.W - xw; + float sw = source.W - xw; // calculate final alpha - float a = xw + bw + sw; + float fw = (sw * 0) + (bw * 1) + (xw * 0); // calculate final value - xform = ((xform * xw) + (backdrop * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + Vector4 xform = ((Vector4.Zero * xw) + (backdrop * bw) + (Vector4.Zero * sw)) / MathF.Max(fw, Constants.Epsilon); + xform.W = fw; return Vector4.Lerp(backdrop, xform, opacity); } @@ -220,18 +220,18 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders 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; + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; + float sw = source.W - xw; // calculate final alpha - float a = xw + bw + sw; + float fw = (sw * 0) + (bw * 0) + (xw * 0); // calculate final value - xform = ((xform * xw) + (Vector4.Zero * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + Vector4 xform = ((Vector4.Zero * xw) + (Vector4.Zero * bw) + (Vector4.Zero * sw)) / MathF.Max(fw, Constants.Epsilon); + xform.W = fw; return Vector4.Lerp(backdrop, xform, opacity); } @@ -240,8 +240,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public static Vector4 Xor(Vector4 backdrop, Vector4 source, float opacity) { opacity = opacity.Clamp(0, 1); - source.W *= opacity; - Vector4 xform = Vector4.Zero; + source.W *= opacity; // calculate weights float xw = backdrop.W * source.W; @@ -249,10 +248,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders float sw = source.W - xw; // calculate final alpha - float a = xw + bw + sw; + float fw = (sw * 1) + (bw * 1) + (xw * 0); // calculate final value - xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + Vector4 xform = ((Vector4.Zero * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(fw, Constants.Epsilon); + xform.W = fw; return Vector4.Lerp(backdrop, xform, opacity); } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index f3e0b1f17..940b585aa 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -40,26 +40,29 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders void GenerateVectorCompositor(string name, string sourceVar, string destVar, string blendVar) { + int a_s = sourceVar == "Vector4.Zero" ? 0 : 1; + int a_b = destVar == "Vector4.Zero" ? 0 : 1; + int a_x = blendVar == "Vector4.Zero" ? 0 : 1; #> [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; + source.W *= opacity; <# } #> - Vector4 xform = <#=blendVar#>; // calculate weights - float xw = <#=destVar#>.W * <#=sourceVar#>.W; - float bw = <#=destVar#>.W - xw; - float sw = <#=sourceVar#>.W - xw; + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; + float sw = source.W - xw; // calculate final alpha - float a = xw + bw + sw; + float fw = (sw * <#=a_s#>) + (bw * <#=a_b#>) + (xw * <#=a_x#>); // calculate final value - xform = ((xform * xw) + (<#=destVar#> * bw) + (<#=sourceVar#> * sw)) / MathF.Max(a, Constants.Epsilon); + Vector4 xform = ((<#=blendVar#> * xw) + (<#=destVar#> * bw) + (<#=sourceVar#> * sw)) / MathF.Max(fw, Constants.Epsilon); + xform.W = fw; return Vector4.Lerp(backdrop, xform, opacity); } From 2b36c6ab00eb5eca3dd9e0800b3f7c6a004e35ba Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 21 Mar 2018 11:55:20 +1100 Subject: [PATCH 5/5] Update tests to add reference comparisons --- .../PixelBlenders/PorterDuffCompositorTests.cs | 13 +++++-------- tests/Images/External | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs index f70fa0a2a..ee2fa2cbe 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs @@ -30,20 +30,17 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders }; [Theory] - [MemberData(nameof(CompositingOperators))] - public void PorterDuffOutputIsCorrect(PixelBlenderMode mode) + [WithFile(TestImages.Png.PDDest, nameof(CompositingOperators), PixelTypes.Rgba32)] + public void PorterDuffOutputIsCorrect(TestImageProvider provider, PixelBlenderMode mode) { - string path = TestEnvironment.CreateOutputDirectory("PorterDuff"); var srcFile = TestFile.Create(TestImages.Png.PDSrc); - var destFile = TestFile.Create(TestImages.Png.PDDest); - using (Image src = srcFile.CreateImage()) - using (Image dest = destFile.CreateImage()) + using (Image dest = provider.GetImage()) { using (Image res = dest.Clone(x => x.Blend(src, new GraphicsOptions { BlenderMode = mode }))) { - // TODO: Generate reference files once this works. - res.Save($"{path}/{mode}.png"); + res.DebugSave(provider, mode.ToString()); + res.CompareToReferenceOutput(provider, mode.ToString()); } } } diff --git a/tests/Images/External b/tests/Images/External index e9f33352b..5a66c9c6d 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit e9f33352b77a5176508d2d5dcafbd1bd33805530 +Subproject commit 5a66c9c6da02bf27345f90adc05d415c0d0450ea