diff --git a/.gitattributes b/.gitattributes index f7bd4d061e..7de2c15977 100644 --- a/.gitattributes +++ b/.gitattributes @@ -85,18 +85,12 @@ ############################################################################### *.basis binary *.dll binary -*.eot binary *.exe binary -*.otf binary *.pdf binary *.ppt binary *.pptx binary *.pvr binary *.snk binary -*.ttc binary -*.ttf binary -*.woff binary -*.woff2 binary *.xls binary *.xlsx binary ############################################################################### @@ -126,6 +120,7 @@ *.dds filter=lfs diff=lfs merge=lfs -text *.ktx filter=lfs diff=lfs merge=lfs -text *.ktx2 filter=lfs diff=lfs merge=lfs -text +*.astc filter=lfs diff=lfs merge=lfs -text *.pam filter=lfs diff=lfs merge=lfs -text *.pbm filter=lfs diff=lfs merge=lfs -text *.pgm filter=lfs diff=lfs merge=lfs -text @@ -143,3 +138,12 @@ # Handle ICC files by git lfs ############################################################################### *.icc filter=lfs diff=lfs merge=lfs -text +############################################################################### +# Handle font files by git lfs +############################################################################### +*.eot filter=lfs diff=lfs merge=lfs -text +*.otf filter=lfs diff=lfs merge=lfs -text +*.ttc filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.woff filter=lfs diff=lfs merge=lfs -text +*.woff2 filter=lfs diff=lfs merge=lfs -text diff --git a/ImageSharp.sln b/ImageSharp.sln index 13dd2fba7e..7a52040374 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 +# Visual Studio Version 18 +VisualStudioVersion = 18.5.11723.231 stable MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_root", "_root", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" ProjectSection(SolutionItems) = preProject @@ -45,6 +45,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp", "src\ImageShar EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{56801022-D71A-4FBE-BC5B-CBA08E2284EC}" ProjectSection(SolutionItems) = preProject + tests\coverlet.runsettings = tests\coverlet.runsettings tests\Directory.Build.props = tests\Directory.Build.props tests\Directory.Build.targets = tests\Directory.Build.targets tests\ImageSharp.Tests.ruleset = tests\ImageSharp.Tests.ruleset diff --git a/shared-infrastructure b/shared-infrastructure index a1d3ac2049..7ac5703452 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit a1d3ac20494631e3cc13132897573796b0e4ee6d +Subproject commit 7ac5703452348d9295db31fc0912c2bd9e419dc9 diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs index 91fa426241..a3959f12e4 100644 --- a/src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs @@ -62,6 +62,7 @@ public override bool Equals(object? obj) /// public override int GetHashCode() => HashCode.Combine( + typeof(GammaWorkingSpace), this.WhitePoint, this.ChromaticityCoordinates, this.Gamma); diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs index 97069b856b..b278dd3177 100644 --- a/src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs @@ -70,8 +70,10 @@ public override bool Equals(object? obj) return true; } - if (obj is RgbWorkingSpace other) + if (obj.GetType() == this.GetType()) { + RgbWorkingSpace other = (RgbWorkingSpace)obj; + return this.WhitePoint.Equals(other.WhitePoint) && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); } @@ -81,5 +83,5 @@ public override bool Equals(object? obj) /// public override int GetHashCode() - => HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates); + => HashCode.Combine(this.GetType(), this.WhitePoint, this.ChromaticityCoordinates); } diff --git a/src/ImageSharp/Common/Tuples/Octet{T}.cs b/src/ImageSharp/Common/Tuples/Octet{T}.cs deleted file mode 100644 index f73bdd3390..0000000000 --- a/src/ImageSharp/Common/Tuples/Octet{T}.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Tuples; - -/// -/// Contains 8 element value tuples of various types. -/// -[StructLayout(LayoutKind.Sequential)] -internal struct Octet - where T : unmanaged -{ - public T V0; - public T V1; - public T V2; - public T V3; - public T V4; - public T V5; - public T V6; - public T V7; - - /// - public override readonly string ToString() - { - return $"Octet<{typeof(T)}>({this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7})"; - } -} - -/// -/// Extension methods for the type. -/// -internal static class OctetExtensions -{ - /// - /// Loads the fields in a target of from one of type. - /// - /// The target of instance. - /// The source of instance. - [MethodImpl(InliningOptions.ShortMethod)] - public static void LoadFrom(ref this Octet destination, ref Octet source) - { - destination.V0 = source.V0; - destination.V1 = source.V1; - destination.V2 = source.V2; - destination.V3 = source.V3; - destination.V4 = source.V4; - destination.V5 = source.V5; - destination.V6 = source.V6; - destination.V7 = source.V7; - } - - /// - /// Loads the fields in a target of from one of type. - /// - /// The target of instance. - /// The source of instance. - [MethodImpl(InliningOptions.ShortMethod)] - public static void LoadFrom(ref this Octet destination, ref Octet source) - { - destination.V0 = (byte)source.V0; - destination.V1 = (byte)source.V1; - destination.V2 = (byte)source.V2; - destination.V3 = (byte)source.V3; - destination.V4 = (byte)source.V4; - destination.V5 = (byte)source.V5; - destination.V6 = (byte)source.V6; - destination.V7 = (byte)source.V7; - } -} diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index 6b25509ed8..71f2bc333c 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -19,11 +19,6 @@ - - diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs index 232d8b3e27..c69510bc4d 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs @@ -7,7 +7,6 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Tuples; namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; @@ -225,8 +224,8 @@ public void Bitops_SingleTuple() // [Benchmark] public void Bitops_Simd() { - ref Octet sBase = ref Unsafe.As>(ref this.source[0]); - ref Octet dBase = ref Unsafe.As>(ref this.dest[0]); + ref InlineArray8 sBase = ref Unsafe.As>(ref this.source[0]); + ref InlineArray8 dBase = ref Unsafe.As>(ref this.dest[0]); for (nuint i = 0; i < (uint)this.Count / 8; i++) { @@ -249,9 +248,9 @@ private struct C #pragma warning restore SA1132 // Do not combine fields [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void BitopsSimdImpl(ref Octet s, ref Octet d) + private static void BitopsSimdImpl(ref InlineArray8 s, ref InlineArray8 d) { - Vector sVec = Unsafe.As, Vector>(ref s); + Vector sVec = Unsafe.As, Vector>(ref s); Vector aMask = new(0xFF00FF00); Vector bMask = new(0x00FF00FF); @@ -274,7 +273,7 @@ private static void BitopsSimdImpl(ref Octet s, ref Octet d) Vector cc = Unsafe.As>(ref c); Vector dd = aa + cc; - d = Unsafe.As, Octet>(ref dd); + d = Unsafe.As, InlineArray8>(ref dd); } // [Benchmark] diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs index da7ddae41e..fd17262dec 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs @@ -5,8 +5,6 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Tuples; - namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; [Config(typeof(Config.Short))] @@ -30,12 +28,22 @@ public void Standard() { const int N = Count / 8; - ref Octet sBase = ref Unsafe.As>(ref this.source[0]); - ref Octet dBase = ref Unsafe.As>(ref this.dest[0]); + ref InlineArray8 sBase = ref Unsafe.As>(ref this.source[0]); + ref InlineArray8 dBase = ref Unsafe.As>(ref this.dest[0]); for (nuint i = 0; i < N; i++) { - Unsafe.Add(ref dBase, i).LoadFrom(ref Unsafe.Add(ref sBase, i)); + ref InlineArray8 source = ref Unsafe.Add(ref sBase, i); + ref InlineArray8 destination = ref Unsafe.Add(ref dBase, i); + + destination[0] = source[0]; + destination[1] = source[1]; + destination[2] = source[2]; + destination[3] = source[3]; + destination[4] = source[4]; + destination[5] = source[5]; + destination[6] = source[6]; + destination[7] = source[7]; } } diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbWorkingSpaceTests.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbWorkingSpaceTests.cs new file mode 100644 index 0000000000..8a38ea11fc --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbWorkingSpaceTests.cs @@ -0,0 +1,122 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; +using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the class. +/// +[Trait("Color", "Conversion")] +public class RgbWorkingSpaceTests +{ + private static readonly ApproximateFloatComparer TolerantComparer = new(1e-5F); + + public static readonly TheoryData WorkingSpaces = new() + { + KnownRgbWorkingSpaces.SRgb, + KnownRgbWorkingSpaces.SRgbSimplified, + KnownRgbWorkingSpaces.Rec709, + KnownRgbWorkingSpaces.Rec2020, + KnownRgbWorkingSpaces.ECIRgbv2, + KnownRgbWorkingSpaces.AdobeRgb1998, + KnownRgbWorkingSpaces.ApplesRgb, + KnownRgbWorkingSpaces.BestRgb, + KnownRgbWorkingSpaces.BetaRgb, + KnownRgbWorkingSpaces.BruceRgb, + KnownRgbWorkingSpaces.CIERgb, + KnownRgbWorkingSpaces.ColorMatchRgb, + KnownRgbWorkingSpaces.DonRgb4, + KnownRgbWorkingSpaces.EktaSpacePS5, + KnownRgbWorkingSpaces.NTSCRgb, + KnownRgbWorkingSpaces.PALSECAMRgb, + KnownRgbWorkingSpaces.ProPhotoRgb, + KnownRgbWorkingSpaces.SMPTECRgb, + KnownRgbWorkingSpaces.WideGamutRgb + }; + + [Fact] + public void RgbWorkingSpaceEqualityRequiresSameConcreteType() + { + RgbWorkingSpace sRgb = KnownRgbWorkingSpaces.SRgb; + RgbWorkingSpace sRgbSimplified = KnownRgbWorkingSpaces.SRgbSimplified; + RgbWorkingSpace rec709 = KnownRgbWorkingSpaces.Rec709; + + Assert.False(sRgb.Equals(sRgbSimplified)); + Assert.False(sRgbSimplified.Equals(sRgb)); + Assert.False(sRgb.Equals(rec709)); + Assert.False(rec709.Equals(sRgb)); + + Assert.NotEqual(sRgb.GetHashCode(), sRgbSimplified.GetHashCode()); + Assert.NotEqual(sRgb.GetHashCode(), rec709.GetHashCode()); + } + + [Fact] + public void RgbWorkingSpaceEqualityMatchesSameConcreteTypeValues() + { + RgbWorkingSpace x = new SRgbWorkingSpace( + KnownRgbWorkingSpaces.SRgb.WhitePoint, + KnownRgbWorkingSpaces.SRgb.ChromaticityCoordinates); + + RgbWorkingSpace y = new SRgbWorkingSpace( + KnownRgbWorkingSpaces.SRgb.WhitePoint, + KnownRgbWorkingSpaces.SRgb.ChromaticityCoordinates); + + Assert.Equal(x, y); + Assert.Equal(x.GetHashCode(), y.GetHashCode()); + } + + [Fact] + public void GammaWorkingSpaceEqualityIncludesGamma() + { + GammaWorkingSpace x = new( + 2.2F, + KnownRgbWorkingSpaces.SRgbSimplified.WhitePoint, + KnownRgbWorkingSpaces.SRgbSimplified.ChromaticityCoordinates); + + GammaWorkingSpace y = new( + 1.8F, + KnownRgbWorkingSpaces.SRgbSimplified.WhitePoint, + KnownRgbWorkingSpaces.SRgbSimplified.ChromaticityCoordinates); + + Assert.NotEqual(x, y); + Assert.NotEqual(x.GetHashCode(), y.GetHashCode()); + } + + [Theory] + [MemberData(nameof(WorkingSpaces))] + public void CompressAndExpand_RoundTripsWithTolerance(RgbWorkingSpace workingSpace) + { + Vector4[] linear = + [ + new(0F, .001F, .18F, 1F), // Endpoint, below the sRGB breakpoint, common middle gray, and opaque alpha. + new(.0031308F, .25F, .5F, .75F), // sRGB linear/gamma breakpoint with interior values and partial alpha. + new(.75F, .5F, .25F, .5F), // Reversed interior values ensure channel order does not hide errors. + new(1F, .8F, .2F, .25F) // Upper endpoint, high/low interior values, and low alpha. + ]; + + Vector4[] expectedCompressed = new Vector4[linear.Length]; + + for (int i = 0; i < linear.Length; i++) + { + Vector4 compressed = workingSpace.Compress(linear[i]); + Vector4 expanded = workingSpace.Expand(compressed); + + expectedCompressed[i] = compressed; + + Assert.Equal(linear[i], expanded, TolerantComparer); + } + + Vector4[] actualCompressed = linear.ToArray(); + workingSpace.Compress(actualCompressed); + + Assert.Equal(expectedCompressed, actualCompressed, TolerantComparer); + + workingSpace.Expand(actualCompressed); + + Assert.Equal(linear, actualCompressed, TolerantComparer); + } +} diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs index 81daac3e0b..12b4c34262 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs @@ -292,7 +292,7 @@ static void RunTest(string serialized) FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } [Theory] @@ -478,7 +478,7 @@ static void RunTest(string serialized) FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } private static void TestShuffleFloat4Channel( diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index e39f9456f0..dfd0a6b307 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -133,7 +133,7 @@ static void RunTest(string serialized) => TestImpl_BulkConvertByteToNormalizedFl FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2); } [Theory] @@ -171,7 +171,7 @@ static void RunTest(string serialized) => TestImpl_BulkConvertNormalizedFloatToB FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 544ac85a51..610693c7f4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -219,7 +219,7 @@ static void RunTest(string srcSeedSerialized, string qtSeedSerialized) RunTest, srcSeed, qtSeed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } [Fact] @@ -391,7 +391,7 @@ static void RunTest() // 3. DisableAvx2 - call fallback code of float implementation FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs index 83c3a344da..b17a39f388 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs @@ -152,7 +152,7 @@ static void RunTest(string serialized) FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, seed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } [Theory] @@ -359,7 +359,7 @@ static void RunTest(string serialized) FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, seed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 8d94fb5cf4..ba0dce4c54 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -69,7 +69,7 @@ public void GetConverterReturnsCorrectConverterWithRgbColorSpace() { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -102,7 +102,7 @@ public void GetConverterReturnsCorrectConverterWithGrayScaleColorSpace() { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -168,7 +168,7 @@ public void GetConverterReturnsCorrectConverterWithYCbCrColorSpace() { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -201,7 +201,7 @@ public void GetConverterReturnsCorrectConverterWithYcckColorSpace() { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs index 924e94d929..ddd79bb756 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -32,6 +33,172 @@ public class PixelBlenderTests { new TestPixel(), typeof(DefaultPixelBlenders.MultiplySrcOver), PixelColorBlendingMode.Multiply }, }; + public static TheoryData BlenderModeMappings + { + get + { + TheoryData data = new(); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.Clear, + typeof(DefaultPixelBlenders.NormalClear), + typeof(DefaultPixelBlenders.MultiplyClear), + typeof(DefaultPixelBlenders.AddClear), + typeof(DefaultPixelBlenders.SubtractClear), + typeof(DefaultPixelBlenders.ScreenClear), + typeof(DefaultPixelBlenders.DarkenClear), + typeof(DefaultPixelBlenders.LightenClear), + typeof(DefaultPixelBlenders.OverlayClear), + typeof(DefaultPixelBlenders.HardLightClear)); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.Xor, + typeof(DefaultPixelBlenders.NormalXor), + typeof(DefaultPixelBlenders.MultiplyXor), + typeof(DefaultPixelBlenders.AddXor), + typeof(DefaultPixelBlenders.SubtractXor), + typeof(DefaultPixelBlenders.ScreenXor), + typeof(DefaultPixelBlenders.DarkenXor), + typeof(DefaultPixelBlenders.LightenXor), + typeof(DefaultPixelBlenders.OverlayXor), + typeof(DefaultPixelBlenders.HardLightXor)); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.Src, + typeof(DefaultPixelBlenders.NormalSrc), + typeof(DefaultPixelBlenders.MultiplySrc), + typeof(DefaultPixelBlenders.AddSrc), + typeof(DefaultPixelBlenders.SubtractSrc), + typeof(DefaultPixelBlenders.ScreenSrc), + typeof(DefaultPixelBlenders.DarkenSrc), + typeof(DefaultPixelBlenders.LightenSrc), + typeof(DefaultPixelBlenders.OverlaySrc), + typeof(DefaultPixelBlenders.HardLightSrc)); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.SrcAtop, + typeof(DefaultPixelBlenders.NormalSrcAtop), + typeof(DefaultPixelBlenders.MultiplySrcAtop), + typeof(DefaultPixelBlenders.AddSrcAtop), + typeof(DefaultPixelBlenders.SubtractSrcAtop), + typeof(DefaultPixelBlenders.ScreenSrcAtop), + typeof(DefaultPixelBlenders.DarkenSrcAtop), + typeof(DefaultPixelBlenders.LightenSrcAtop), + typeof(DefaultPixelBlenders.OverlaySrcAtop), + typeof(DefaultPixelBlenders.HardLightSrcAtop)); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.SrcIn, + typeof(DefaultPixelBlenders.NormalSrcIn), + typeof(DefaultPixelBlenders.MultiplySrcIn), + typeof(DefaultPixelBlenders.AddSrcIn), + typeof(DefaultPixelBlenders.SubtractSrcIn), + typeof(DefaultPixelBlenders.ScreenSrcIn), + typeof(DefaultPixelBlenders.DarkenSrcIn), + typeof(DefaultPixelBlenders.LightenSrcIn), + typeof(DefaultPixelBlenders.OverlaySrcIn), + typeof(DefaultPixelBlenders.HardLightSrcIn)); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.SrcOut, + typeof(DefaultPixelBlenders.NormalSrcOut), + typeof(DefaultPixelBlenders.MultiplySrcOut), + typeof(DefaultPixelBlenders.AddSrcOut), + typeof(DefaultPixelBlenders.SubtractSrcOut), + typeof(DefaultPixelBlenders.ScreenSrcOut), + typeof(DefaultPixelBlenders.DarkenSrcOut), + typeof(DefaultPixelBlenders.LightenSrcOut), + typeof(DefaultPixelBlenders.OverlaySrcOut), + typeof(DefaultPixelBlenders.HardLightSrcOut)); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.Dest, + typeof(DefaultPixelBlenders.NormalDest), + typeof(DefaultPixelBlenders.MultiplyDest), + typeof(DefaultPixelBlenders.AddDest), + typeof(DefaultPixelBlenders.SubtractDest), + typeof(DefaultPixelBlenders.ScreenDest), + typeof(DefaultPixelBlenders.DarkenDest), + typeof(DefaultPixelBlenders.LightenDest), + typeof(DefaultPixelBlenders.OverlayDest), + typeof(DefaultPixelBlenders.HardLightDest)); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.DestAtop, + typeof(DefaultPixelBlenders.NormalDestAtop), + typeof(DefaultPixelBlenders.MultiplyDestAtop), + typeof(DefaultPixelBlenders.AddDestAtop), + typeof(DefaultPixelBlenders.SubtractDestAtop), + typeof(DefaultPixelBlenders.ScreenDestAtop), + typeof(DefaultPixelBlenders.DarkenDestAtop), + typeof(DefaultPixelBlenders.LightenDestAtop), + typeof(DefaultPixelBlenders.OverlayDestAtop), + typeof(DefaultPixelBlenders.HardLightDestAtop)); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.DestIn, + typeof(DefaultPixelBlenders.NormalDestIn), + typeof(DefaultPixelBlenders.MultiplyDestIn), + typeof(DefaultPixelBlenders.AddDestIn), + typeof(DefaultPixelBlenders.SubtractDestIn), + typeof(DefaultPixelBlenders.ScreenDestIn), + typeof(DefaultPixelBlenders.DarkenDestIn), + typeof(DefaultPixelBlenders.LightenDestIn), + typeof(DefaultPixelBlenders.OverlayDestIn), + typeof(DefaultPixelBlenders.HardLightDestIn)); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.DestOut, + typeof(DefaultPixelBlenders.NormalDestOut), + typeof(DefaultPixelBlenders.MultiplyDestOut), + typeof(DefaultPixelBlenders.AddDestOut), + typeof(DefaultPixelBlenders.SubtractDestOut), + typeof(DefaultPixelBlenders.ScreenDestOut), + typeof(DefaultPixelBlenders.DarkenDestOut), + typeof(DefaultPixelBlenders.LightenDestOut), + typeof(DefaultPixelBlenders.OverlayDestOut), + typeof(DefaultPixelBlenders.HardLightDestOut)); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.DestOver, + typeof(DefaultPixelBlenders.NormalDestOver), + typeof(DefaultPixelBlenders.MultiplyDestOver), + typeof(DefaultPixelBlenders.AddDestOver), + typeof(DefaultPixelBlenders.SubtractDestOver), + typeof(DefaultPixelBlenders.ScreenDestOver), + typeof(DefaultPixelBlenders.DarkenDestOver), + typeof(DefaultPixelBlenders.LightenDestOver), + typeof(DefaultPixelBlenders.OverlayDestOver), + typeof(DefaultPixelBlenders.HardLightDestOver)); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.SrcOver, + typeof(DefaultPixelBlenders.NormalSrcOver), + typeof(DefaultPixelBlenders.MultiplySrcOver), + typeof(DefaultPixelBlenders.AddSrcOver), + typeof(DefaultPixelBlenders.SubtractSrcOver), + typeof(DefaultPixelBlenders.ScreenSrcOver), + typeof(DefaultPixelBlenders.DarkenSrcOver), + typeof(DefaultPixelBlenders.LightenSrcOver), + typeof(DefaultPixelBlenders.OverlaySrcOver), + typeof(DefaultPixelBlenders.HardLightSrcOver)); + + return data; + } + } + [Theory] [MemberData(nameof(BlenderMappings))] public void ReturnsCorrectBlender(TestPixel pixel, Type type, PixelColorBlendingMode mode) @@ -41,6 +208,126 @@ public void ReturnsCorrectBlender(TestPixel pixel, Type type, Pi Assert.IsType(type, blender); } + [Theory] + [MemberData(nameof(BlenderModeMappings))] + public void ReturnsCorrectBlenderForAllModeCombinations(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode, Type type) + { + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(colorMode, alphaMode); + Assert.IsType(type, blender); + } + + [Fact] + public void BlendFunctionsAreCalledForAllModeCombinations() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + ExerciseAllBlenderModeCombinations, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + [Fact] + public void Blend_WithConstantSourceAndSingleAmount() + { + PixelBlender blender = new DefaultPixelBlenders.NormalSrcOver(); + Rgba32[] destination = new Rgba32[2]; + Rgba32[] background = + [ + Color.Red.ToPixel(), + Color.Green.ToPixel() + ]; + + Rgba32 source = Color.Blue.ToPixel(); + + blender.Blend(Configuration.Default, destination, background, source, 1F); + + Assert.Equal(source, destination[0]); + Assert.Equal(source, destination[1]); + } + + [Fact] + public void Blend_WithConstantSourceSingleAmountAndWorkingBuffer() + { + PixelBlender blender = new DefaultPixelBlenders.NormalSrcOver(); + Rgba32[] destination = new Rgba32[2]; + Rgba32[] background = + [ + Color.Red.ToPixel(), + Color.Green.ToPixel() + ]; + + Rgba32 source = Color.Blue.ToPixel(); + Vector4[] workingBuffer = new Vector4[destination.Length * 2]; + + blender.Blend(Configuration.Default, destination, background, source, 1F, workingBuffer); + + Assert.Equal(source, destination[0]); + Assert.Equal(source, destination[1]); + } + + [Fact] + public void Blend_WithConstantSourceAndAmountSpan() + { + PixelBlender blender = new DefaultPixelBlenders.NormalSrcOver(); + Rgba32[] destination = new Rgba32[2]; + Rgba32[] background = + [ + Color.Red.ToPixel(), + Color.Green.ToPixel() + ]; + + Rgba32 source = Color.Blue.ToPixel(); + float[] amount = [1F, 1F]; + + blender.Blend(Configuration.Default, destination, background, source, amount); + + Assert.Equal(source, destination[0]); + Assert.Equal(source, destination[1]); + } + + [Fact] + public void Blend_WithConstantSourceAmountSpanAndWorkingBuffer() + { + PixelBlender blender = new DefaultPixelBlenders.NormalSrcOver(); + Rgba32[] destination = new Rgba32[2]; + Rgba32[] background = + [ + Color.Red.ToPixel(), + Color.Green.ToPixel() + ]; + + Rgba32 source = Color.Blue.ToPixel(); + float[] amount = [1F, 1F]; + Vector4[] workingBuffer = new Vector4[destination.Length * 2]; + + blender.Blend(Configuration.Default, destination, background, source, amount, workingBuffer); + + Assert.Equal(source, destination[0]); + Assert.Equal(source, destination[1]); + } + + [Fact] + public void Blend_WithSourceSpanAmountSpanAndWorkingBuffer() + { + PixelBlender blender = new DefaultPixelBlenders.NormalSrcOver(); + Rgba32[] destination = new Rgba32[2]; + Rgba32[] background = + [ + Color.Red.ToPixel(), + Color.Green.ToPixel() + ]; + + Rgba32[] source = + [ + Color.Blue.ToPixel(), + Color.Yellow.ToPixel() + ]; + + float[] amount = [1F, 1F]; + Vector4[] workingBuffer = new Vector4[destination.Length * 3]; + + blender.Blend(Configuration.Default, destination, background, source, amount, workingBuffer); + + Assert.Equal(source[0], destination[0]); + Assert.Equal(source[1], destination[1]); + } + public static TheoryData ColorBlendingExpectedResults = new() { { Color.MistyRose.ToPixel(), Color.MidnightBlue.ToPixel(), 1, PixelColorBlendingMode.Normal, Color.MidnightBlue.ToPixel() }, @@ -92,4 +379,67 @@ public void TestAlphaCompositionModes(Rgba32 backdrop, Rgba32 source, float opac // var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4()); } + + private static void AddBlenderModeMappings( + TheoryData data, + PixelAlphaCompositionMode alphaMode, + Type normal, + Type multiply, + Type add, + Type subtract, + Type screen, + Type darken, + Type lighten, + Type overlay, + Type hardLight) + { + data.Add(PixelColorBlendingMode.Normal, alphaMode, normal); + data.Add(PixelColorBlendingMode.Multiply, alphaMode, multiply); + data.Add(PixelColorBlendingMode.Add, alphaMode, add); + data.Add(PixelColorBlendingMode.Subtract, alphaMode, subtract); + data.Add(PixelColorBlendingMode.Screen, alphaMode, screen); + data.Add(PixelColorBlendingMode.Darken, alphaMode, darken); + data.Add(PixelColorBlendingMode.Lighten, alphaMode, lighten); + data.Add(PixelColorBlendingMode.Overlay, alphaMode, overlay); + data.Add(PixelColorBlendingMode.HardLight, alphaMode, hardLight); + } + + private static void ExerciseAllBlenderModeCombinations() + { + foreach (PixelAlphaCompositionMode alphaMode in Enum.GetValues()) + { + foreach (PixelColorBlendingMode colorMode in Enum.GetValues()) + { + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(colorMode, alphaMode); + ExerciseBlender(blender); + } + } + } + + private static void ExerciseBlender(PixelBlender blender) + { + Rgba32 background = Color.MistyRose.ToPixel(); + Rgba32 source = Color.MidnightBlue.ToPixel(); + float[] amount = [1F, 1F, 1F, 1F]; + + Rgba32 expected = blender.Blend(background, source, 1F); + + Rgba32[] destination = new Rgba32[4]; + Rgba32[] backgroundSpan = [background, background, background, background]; + Rgba32[] sourceSpan = [source, source, source, source]; + Vector4[] sourceSpanBuffer = new Vector4[destination.Length * 3]; + Vector4[] constantSourceBuffer = new Vector4[destination.Length * 2]; + + blender.Blend(Configuration.Default, destination, backgroundSpan, sourceSpan, 1F, sourceSpanBuffer); + Assert.All(destination, x => Assert.Equal(expected, x)); + + blender.Blend(Configuration.Default, destination, backgroundSpan, source, 1F, constantSourceBuffer); + Assert.All(destination, x => Assert.Equal(expected, x)); + + blender.Blend(Configuration.Default, destination, backgroundSpan, sourceSpan, amount, sourceSpanBuffer); + Assert.All(destination, x => Assert.Equal(expected, x)); + + blender.Blend(Configuration.Default, destination, backgroundSpan, source, amount, constantSourceBuffer); + Assert.All(destination, x => Assert.Equal(expected, x)); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs index 994b7d02ee..f8c296846b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs @@ -59,7 +59,7 @@ TestImageProvider provider FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX, provider, mode.ToString()); } diff --git a/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs b/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs index be3e9ccd5d..4cd30fc571 100644 --- a/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs +++ b/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs @@ -439,7 +439,7 @@ public enum HwIntrinsics : long DisableSSE42 = 1L << 1, DisableAVX = 1L << 2, DisableAVX2 = 1L << 3, - DisableAVX512 = 1L << 4, + DisableAVX512F = 1L << 4, DisableAVX512v2 = 1L << 5, DisableAVX512v3 = 1L << 6, DisableAVX10v1 = 1L << 7, diff --git a/tests/coverlet.runsettings b/tests/coverlet.runsettings index cffce3540b..f327b47a2e 100644 --- a/tests/coverlet.runsettings +++ b/tests/coverlet.runsettings @@ -9,7 +9,7 @@ lcov - [SixLabors.*]* + [SixLabors.ImageSharp*]* true