// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { public class PorterDuffFunctionsTestsTPixel { private static Span AsSpan(T value) where T : struct { return new Span(new[] { value }); } 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) } }; private Configuration Configuration => Configuration.Default; [Theory] [MemberData(nameof(NormalBlendFunctionData))] public void NormalBlendFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { TPixel actual = PorterDuffFunctions.NormalSrcOver(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(NormalBlendFunctionData))] public void NormalBlendFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { TPixel actual = new DefaultPixelBlenders.NormalSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(NormalBlendFunctionData))] public void NormalBlendFunctionBlenderBulk(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.NormalSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } 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 : unmanaged, IPixel { TPixel actual = PorterDuffFunctions.MultiplySrcOver(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(MultiplyFunctionData))] public void MultiplyFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { TPixel actual = new DefaultPixelBlenders.MultiplySrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(MultiplyFunctionData))] public void MultiplyFunctionBlenderBulk(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.MultiplySrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected.AsPixel(), dest[0], 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(1f, 1f, 1f, 1f) }, { new TestPixel(0.2f, 0.2f, 0.2f, 0.3f), new TestPixel(0.3f, 0.3f, 0.3f, 0.2f), .5f, new TestPixel(.2431373f, .2431373f, .2431373f, .372549f) } }; [Theory] [MemberData(nameof(AddFunctionData))] public void AddFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { TPixel actual = PorterDuffFunctions.AddSrcOver(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(AddFunctionData))] public void AddFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { TPixel actual = new DefaultPixelBlenders.AddSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(AddFunctionData))] public void AddFunctionBlenderBulk(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.AddSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData SubtractFunctionData = 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(SubtractFunctionData))] public void SubtractFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { TPixel actual = PorterDuffFunctions.SubtractSrcOver(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(SubtractFunctionData))] public void SubtractFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { TPixel actual = new DefaultPixelBlenders.SubtractSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(SubtractFunctionData))] public void SubtractFunctionBlenderBulk(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.SubtractSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected.AsPixel(), dest[0], 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 : unmanaged, IPixel { TPixel actual = PorterDuffFunctions.ScreenSrcOver(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(ScreenFunctionData))] public void ScreenFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { TPixel actual = new DefaultPixelBlenders.ScreenSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(ScreenFunctionData))] public void ScreenFunctionBlenderBulk(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.ScreenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected.AsPixel(), dest[0], 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 : unmanaged, IPixel { TPixel actual = PorterDuffFunctions.DarkenSrcOver(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(DarkenFunctionData))] public void DarkenFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { TPixel actual = new DefaultPixelBlenders.DarkenSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(DarkenFunctionData))] public void DarkenFunctionBlenderBulk(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.DarkenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected.AsPixel(), dest[0], 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 : unmanaged, IPixel { TPixel actual = PorterDuffFunctions.LightenSrcOver(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(LightenFunctionData))] public void LightenFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { TPixel actual = new DefaultPixelBlenders.LightenSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(LightenFunctionData))] public void LightenFunctionBlenderBulk(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.LightenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected.AsPixel(), dest[0], 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 : unmanaged, IPixel { TPixel actual = PorterDuffFunctions.OverlaySrcOver(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(OverlayFunctionData))] public void OverlayFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { TPixel actual = new DefaultPixelBlenders.OverlaySrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(OverlayFunctionData))] public void OverlayFunctionBlenderBulk(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.OverlaySrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected.AsPixel(), dest[0], 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 : unmanaged, IPixel { TPixel actual = PorterDuffFunctions.HardLightSrcOver(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(HardLightFunctionData))] public void HardLightFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { TPixel actual = new DefaultPixelBlenders.HardLightSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] [MemberData(nameof(HardLightFunctionData))] public void HardLightFunctionBlenderBulk(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.HardLightSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } } }