From bfed55d4a11281ba2dcb3753accf262c6b9f7f3c Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 30 Apr 2017 11:21:42 +0100 Subject: [PATCH] PorterDuff tests --- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 3 - .../PixelBlenders/PorterDuffFunctionsTests.cs | 183 +++++++++++++++++ .../PorterDuffFunctionsTests_TPixel.cs | 193 ++++++++++++++++++ .../PixelFormats/PixelOperations.cs | 5 +- .../TestUtilities/TestVector4.cs | 60 ++++++ tests/ImageSharp.Tests/VectorAssert.cs | 98 +++++++++ 6 files changed, 538 insertions(+), 4 deletions(-) create mode 100644 tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs create mode 100644 tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/TestVector4.cs create mode 100644 tests/ImageSharp.Tests/VectorAssert.cs diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 8dd2f2790..55b3c80e3 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -27,7 +27,4 @@ PreserveNewest - - - \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs new file mode 100644 index 000000000..45962c589 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs @@ -0,0 +1,183 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.PixelFormats.PixelBlenders +{ + using System; + using System.Collections.Generic; + using System.Numerics; + using System.Text; + using ImageSharp.PixelFormats.PixelBlenders; + using ImageSharp.Tests.TestUtilities; + using Xunit; + + public class PorterDuffFunctionsTests + { + public static TheoryData NormalBlendFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(0.6f, 0.6f, 0.6f, 1) }, + }; + + [Theory] + [MemberData(nameof(NormalBlendFunctionData))] + public void NormalBlendFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.NormalBlendFunction(back, source, amount); + Assert.Equal(expected, actual); + } + + public static TheoryData MultiplyFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(0.6f, 0.6f, 0.6f, 1) }, + { + new TestVector4(0.9f,0.9f,0.9f,0.9f), + new TestVector4(0.4f,0.4f,0.4f,0.4f), + .5f, + new TestVector4(0.7834783f, 0.7834783f, 0.7834783f, 0.92f) + }, + }; + + [Theory] + [MemberData(nameof(MultiplyFunctionData))] + public void MultiplyFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.MultiplyFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData AddFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(.6f, .6f, .6f, 1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.2075676f, .2075676f, .2075676f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(AddFunctionData))] + public void AddFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.MultiplyFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData SubstractFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(0,0,0,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(1,1,1, 1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.2027027f, .2027027f, .2027027f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(SubstractFunctionData))] + public void SubstractFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.SubstractFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData ScreenFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(1,1,1, 1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.2383784f, .2383784f, .2383784f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(ScreenFunctionData))] + public void ScreenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.ScreenFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData DarkenFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(.6f,.6f,.6f, 1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.2189189f, .2189189f, .2189189f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(DarkenFunctionData))] + public void DarkenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.DarkenFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData LightenFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(1,1,1,1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.227027f, .227027f, .227027f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(LightenFunctionData))] + public void LightenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.LightenFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData OverlayFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(1,1,1,1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.2124324f, .2124324f, .2124324f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(OverlayFunctionData))] + public void OverlayFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.OverlayFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData HardLightFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(0.6f,0.6f,0.6f,1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.2124324f, .2124324f, .2124324f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(HardLightFunctionData))] + public void HardLightFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.HardLightFunction(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 new file mode 100644 index 000000000..168269e51 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -0,0 +1,193 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.PixelFormats.PixelBlenders +{ + using System; + using System.Collections.Generic; + using System.Numerics; + using System.Text; + using ImageSharp.PixelFormats; + using ImageSharp.PixelFormats.PixelBlenders; + using ImageSharp.Tests.TestUtilities; + using Xunit; + + public class PorterDuffFunctionsTests_TPixel + { + public static TheoryData NormalBlendFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(0.6f, 0.6f, 0.6f, 1) }, + }; + + [Theory] + [MemberData(nameof(NormalBlendFunctionData))] + public void NormalBlendFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.NormalBlendFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 3); + } + + public static TheoryData MultiplyFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(0.6f, 0.6f, 0.6f, 1) }, + { + new TestPixel(0.9f,0.9f,0.9f,0.9f), + new TestPixel(0.4f,0.4f,0.4f,0.4f), + .5f, + new TestPixel(0.7834783f, 0.7834783f, 0.7834783f, 0.92f) + }, + }; + + [Theory] + [MemberData(nameof(MultiplyFunctionData))] + public void MultiplyFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.MultiplyFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + public static TheoryData AddFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(.6f, .6f, .6f, 1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.2075676f, .2075676f, .2075676f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(AddFunctionData))] + public void AddFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.MultiplyFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + public static TheoryData SubstractFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(0,0,0,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(1,1,1, 1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.2027027f, .2027027f, .2027027f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(SubstractFunctionData))] + public void SubstractFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.SubstractFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + public static TheoryData ScreenFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(1,1,1, 1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.2383784f, .2383784f, .2383784f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(ScreenFunctionData))] + public void ScreenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.ScreenFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + public static TheoryData DarkenFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(.6f,.6f,.6f, 1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.2189189f, .2189189f, .2189189f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(DarkenFunctionData))] + public void DarkenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.DarkenFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + public static TheoryData LightenFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(1,1,1,1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.227027f, .227027f, .227027f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(LightenFunctionData))] + public void LightenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.LightenFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + public static TheoryData OverlayFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(1,1,1,1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.2124324f, .2124324f, .2124324f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(OverlayFunctionData))] + public void OverlayFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.OverlayFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + public static TheoryData HardLightFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(0.6f,0.6f,0.6f,1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.2124324f, .2124324f, .2124324f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(HardLightFunctionData))] + public void HardLightFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.HardLightFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs index 0618114a0..a9108692e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs @@ -1,4 +1,7 @@ - +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// namespace ImageSharp.Tests.PixelFormats { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs b/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs new file mode 100644 index 000000000..beb3fcd97 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Text; +using ImageSharp.PixelFormats; +using Xunit.Abstractions; + +namespace ImageSharp.Tests.TestUtilities +{ + public class TestVector4 : IXunitSerializable + { + public TestVector4() + { + } + + public TestVector4(float x, float y, float z, float w) + { + this.X = x; + this.Y = y; + this.Z = x; + this.W = w; + } + + public float X { get; set; } + public float Y { get; set; } + public float Z { get; set; } + public float W { get; set; } + + public static implicit operator Vector4(TestVector4 d) + { + return d?.AsVector() ?? default(Vector4); + } + + public Vector4 AsVector() + { + return new Vector4(this.X, this.Y, this.Z, this.W); + } + + public void Deserialize(IXunitSerializationInfo info) + { + this.X = info.GetValue("x"); + this.Y = info.GetValue("y"); + this.Z= info.GetValue("z"); + this.W= info.GetValue("w"); + } + + public void Serialize(IXunitSerializationInfo info) + { + info.AddValue("x", this.X); + info.AddValue("y", this.Y); + info.AddValue("z", this.Z); + info.AddValue("w", this.W); + } + + public override string ToString() + { + return $"{this.AsVector().ToString()}"; + } + } +} diff --git a/tests/ImageSharp.Tests/VectorAssert.cs b/tests/ImageSharp.Tests/VectorAssert.cs new file mode 100644 index 000000000..ad26963d4 --- /dev/null +++ b/tests/ImageSharp.Tests/VectorAssert.cs @@ -0,0 +1,98 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +// ReSharper disable MemberHidesStaticFromOuterClass +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + using System.Numerics; + using ImageSharp; + using ImageSharp.PixelFormats; + using Xunit; + + /// + /// Class to perform simple image comparisons. + /// + public static class VectorAssert + { + public static void Equal(TPixel expected, TPixel actual, int precision = int.MaxValue) + where TPixel : struct, IPixel + { + Equal(expected.ToVector4(), actual.ToVector4(), precision); + } + + public static void Equal(Vector4 expected, Vector4 actual, int precision = int.MaxValue) + { + Assert.Equal(expected, actual, new PrecisionEqualityComparer(precision)); + } + + public static void Equal(Vector3 expected, Vector3 actual, int precision = int.MaxValue) + { + Assert.Equal(expected, actual, new PrecisionEqualityComparer(precision)); + } + + public static void Equal(Vector2 expected, Vector2 actual, int precision = int.MaxValue) + { + Assert.Equal(expected, actual, new PrecisionEqualityComparer(precision)); + } + + private struct PrecisionEqualityComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer + { + private readonly int precision; + + public PrecisionEqualityComparer(int precision) + { + this.precision = precision; + } + + public bool Equals(Vector2 x, Vector2 y) + { + return Equals(x.X, y.X) && + Equals(x.Y, y.Y); + + } + public bool Equals(Vector3 x, Vector3 y) + { + return Equals(x.X, y.X) && + Equals(x.Y, y.Y) && + Equals(x.Z, y.Z); + + } + + public bool Equals(Vector4 x, Vector4 y) + { + return Equals(x.W, y.W) && + Equals(x.X, y.X) && + Equals(x.Y, y.Y) && + Equals(x.Z, y.Z); + + } + + public bool Equals(float x, float y) + { + return Math.Round(x, this.precision) == Math.Round(y, this.precision); + } + + public int GetHashCode(Vector4 obj) + { + return obj.GetHashCode(); + } + public int GetHashCode(Vector3 obj) + { + return obj.GetHashCode(); + } + public int GetHashCode(Vector2 obj) + { + return obj.GetHashCode(); + } + + public int GetHashCode(float obj) + { + return obj.GetHashCode(); + } + } + } +}