Browse Source

Add additional unit tests to improve code coverage. Update licensing version

pull/3127/head
James Jackson-South 3 months ago
parent
commit
1e65e08fc1
  1. 16
      .gitattributes
  2. 5
      ImageSharp.sln
  3. 2
      shared-infrastructure
  4. 1
      src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs
  5. 6
      src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs
  6. 72
      src/ImageSharp/Common/Tuples/Octet{T}.cs
  7. 5
      tests/Directory.Build.targets
  8. 11
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs
  9. 18
      tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs
  10. 122
      tests/ImageSharp.Tests/ColorProfiles/RgbWorkingSpaceTests.cs
  11. 4
      tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs
  12. 4
      tests/ImageSharp.Tests/Common/SimdUtilsTests.cs
  13. 4
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  14. 4
      tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs
  15. 8
      tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
  16. 350
      tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs
  17. 2
      tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs
  18. 2
      tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs
  19. 2
      tests/coverlet.runsettings

16
.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

5
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

2
shared-infrastructure

@ -1 +1 @@
Subproject commit a1d3ac20494631e3cc13132897573796b0e4ee6d
Subproject commit 7ac5703452348d9295db31fc0912c2bd9e419dc9

1
src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs

@ -62,6 +62,7 @@ public sealed class GammaWorkingSpace : RgbWorkingSpace
/// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(
typeof(GammaWorkingSpace),
this.WhitePoint,
this.ChromaticityCoordinates,
this.Gamma);

6
src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs

@ -70,8 +70,10 @@ public abstract class RgbWorkingSpace
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 abstract class RgbWorkingSpace
/// <inheritdoc/>
public override int GetHashCode()
=> HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates);
=> HashCode.Combine(this.GetType(), this.WhitePoint, this.ChromaticityCoordinates);
}

72
src/ImageSharp/Common/Tuples/Octet{T}.cs

@ -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;
/// <summary>
/// Contains 8 element value tuples of various types.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct Octet<T>
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;
/// <inheritdoc/>
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})";
}
}
/// <summary>
/// Extension methods for the <see cref="Octet{T}"/> type.
/// </summary>
internal static class OctetExtensions
{
/// <summary>
/// Loads the fields in a target <see cref="Octet{T}"/> of <see cref="uint"/> from one of <see cref="byte"/> type.
/// </summary>
/// <param name="destination">The target <see cref="Octet{T}"/> of <see cref="uint"/> instance.</param>
/// <param name="source">The source <see cref="Octet{T}"/> of <see cref="byte"/> instance.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void LoadFrom(ref this Octet<uint> destination, ref Octet<byte> 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;
}
/// <summary>
/// Loads the fields in a target <see cref="Octet{T}"/> of <see cref="byte"/> from one of <see cref="uint"/> type.
/// </summary>
/// <param name="destination">The target <see cref="Octet{T}"/> of <see cref="byte"/> instance.</param>
/// <param name="source">The source <see cref="Octet{T}"/> of <see cref="uint"/> instance.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void LoadFrom(ref this Octet<byte> destination, ref Octet<uint> 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;
}
}

5
tests/Directory.Build.targets

@ -19,11 +19,6 @@
<ItemGroup>
<!-- Test Dependencies -->
<PackageReference Update="Colourful" Version="3.2.0" />
<!--
Do not update to 14+ yet. There's differnce in how the BMP decoder handles rounding in 16 bit images.
See https://github.com/ImageMagick/ImageMagick/commit/27a0a9c37f18af9c8d823a3ea076f600843b553c
-->
<PackageReference Update="Magick.NET-Q16-AnyCPU" Version="14.11.1" />
<PackageReference Update="Microsoft.DotNet.RemoteExecutor" Version="10.0.0-beta.25563.105" />
<PackageReference Update="Microsoft.DotNet.XUnitExtensions" Version="8.0.0-beta.23580.1" />

11
tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs

@ -7,7 +7,6 @@ using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tuples;
namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion;
@ -225,8 +224,8 @@ public class PixelConversion_Rgba32_To_Bgra32
// [Benchmark]
public void Bitops_Simd()
{
ref Octet<uint> sBase = ref Unsafe.As<Rgba32, Octet<uint>>(ref this.source[0]);
ref Octet<uint> dBase = ref Unsafe.As<Bgra32, Octet<uint>>(ref this.dest[0]);
ref InlineArray8<uint> sBase = ref Unsafe.As<Rgba32, InlineArray8<uint>>(ref this.source[0]);
ref InlineArray8<uint> dBase = ref Unsafe.As<Bgra32, InlineArray8<uint>>(ref this.dest[0]);
for (nuint i = 0; i < (uint)this.Count / 8; i++)
{
@ -249,9 +248,9 @@ public class PixelConversion_Rgba32_To_Bgra32
#pragma warning restore SA1132 // Do not combine fields
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void BitopsSimdImpl(ref Octet<uint> s, ref Octet<uint> d)
private static void BitopsSimdImpl(ref InlineArray8<uint> s, ref InlineArray8<uint> d)
{
Vector<uint> sVec = Unsafe.As<Octet<uint>, Vector<uint>>(ref s);
Vector<uint> sVec = Unsafe.As<InlineArray8<uint>, Vector<uint>>(ref s);
Vector<uint> aMask = new(0xFF00FF00);
Vector<uint> bMask = new(0x00FF00FF);
@ -274,7 +273,7 @@ public class PixelConversion_Rgba32_To_Bgra32
Vector<uint> cc = Unsafe.As<C, Vector<uint>>(ref c);
Vector<uint> dd = aa + cc;
d = Unsafe.As<Vector<uint>, Octet<uint>>(ref dd);
d = Unsafe.As<Vector<uint>, InlineArray8<uint>>(ref dd);
}
// [Benchmark]

18
tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs

@ -5,8 +5,6 @@ using System.Numerics;
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 class WidenBytesToUInt32
{
const int N = Count / 8;
ref Octet<byte> sBase = ref Unsafe.As<byte, Octet<byte>>(ref this.source[0]);
ref Octet<uint> dBase = ref Unsafe.As<uint, Octet<uint>>(ref this.dest[0]);
ref InlineArray8<byte> sBase = ref Unsafe.As<byte, InlineArray8<byte>>(ref this.source[0]);
ref InlineArray8<uint> dBase = ref Unsafe.As<uint, InlineArray8<uint>>(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<byte> source = ref Unsafe.Add(ref sBase, i);
ref InlineArray8<uint> 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];
}
}

122
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;
/// <summary>
/// Tests the <see cref="RgbWorkingSpace"/> class.
/// </summary>
[Trait("Color", "Conversion")]
public class RgbWorkingSpaceTests
{
private static readonly ApproximateFloatComparer TolerantComparer = new(1e-5F);
public static readonly TheoryData<RgbWorkingSpace> 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);
}
}

4
tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs

@ -292,7 +292,7 @@ public partial class SimdUtilsTests
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
count,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic);
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic);
}
[Theory]
@ -478,7 +478,7 @@ public partial class SimdUtilsTests
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
count,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic);
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic);
}
private static void TestShuffleFloat4Channel(

4
tests/ImageSharp.Tests/Common/SimdUtilsTests.cs

@ -133,7 +133,7 @@ public partial class SimdUtilsTests
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
count,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2);
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2);
}
[Theory]
@ -171,7 +171,7 @@ public partial class SimdUtilsTests
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
count,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2);
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2);
}
[Theory]

4
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

@ -219,7 +219,7 @@ public partial class Block8x8FTests : JpegFixture
RunTest,
srcSeed,
qtSeed,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic);
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic);
}
[Fact]
@ -391,7 +391,7 @@ public partial class Block8x8FTests : JpegFixture
// 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]

4
tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs

@ -152,7 +152,7 @@ public static class DCTTests
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
seed,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic);
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic);
}
[Theory]
@ -359,7 +359,7 @@ public static class DCTTests
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
seed,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic);
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic);
}
}
}

8
tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs

@ -69,7 +69,7 @@ public class JpegColorConverterTests
{
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 class JpegColorConverterTests
{
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 class JpegColorConverterTests
{
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 class JpegColorConverterTests
{
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic);
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic);
static void RunTest(string arg)
{

350
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<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.MultiplySrcOver), PixelColorBlendingMode.Multiply },
};
public static TheoryData<PixelColorBlendingMode, PixelAlphaCompositionMode, Type> BlenderModeMappings
{
get
{
TheoryData<PixelColorBlendingMode, PixelAlphaCompositionMode, Type> data = new();
AddBlenderModeMappings(
data,
PixelAlphaCompositionMode.Clear,
typeof(DefaultPixelBlenders<Rgba32>.NormalClear),
typeof(DefaultPixelBlenders<Rgba32>.MultiplyClear),
typeof(DefaultPixelBlenders<Rgba32>.AddClear),
typeof(DefaultPixelBlenders<Rgba32>.SubtractClear),
typeof(DefaultPixelBlenders<Rgba32>.ScreenClear),
typeof(DefaultPixelBlenders<Rgba32>.DarkenClear),
typeof(DefaultPixelBlenders<Rgba32>.LightenClear),
typeof(DefaultPixelBlenders<Rgba32>.OverlayClear),
typeof(DefaultPixelBlenders<Rgba32>.HardLightClear));
AddBlenderModeMappings(
data,
PixelAlphaCompositionMode.Xor,
typeof(DefaultPixelBlenders<Rgba32>.NormalXor),
typeof(DefaultPixelBlenders<Rgba32>.MultiplyXor),
typeof(DefaultPixelBlenders<Rgba32>.AddXor),
typeof(DefaultPixelBlenders<Rgba32>.SubtractXor),
typeof(DefaultPixelBlenders<Rgba32>.ScreenXor),
typeof(DefaultPixelBlenders<Rgba32>.DarkenXor),
typeof(DefaultPixelBlenders<Rgba32>.LightenXor),
typeof(DefaultPixelBlenders<Rgba32>.OverlayXor),
typeof(DefaultPixelBlenders<Rgba32>.HardLightXor));
AddBlenderModeMappings(
data,
PixelAlphaCompositionMode.Src,
typeof(DefaultPixelBlenders<Rgba32>.NormalSrc),
typeof(DefaultPixelBlenders<Rgba32>.MultiplySrc),
typeof(DefaultPixelBlenders<Rgba32>.AddSrc),
typeof(DefaultPixelBlenders<Rgba32>.SubtractSrc),
typeof(DefaultPixelBlenders<Rgba32>.ScreenSrc),
typeof(DefaultPixelBlenders<Rgba32>.DarkenSrc),
typeof(DefaultPixelBlenders<Rgba32>.LightenSrc),
typeof(DefaultPixelBlenders<Rgba32>.OverlaySrc),
typeof(DefaultPixelBlenders<Rgba32>.HardLightSrc));
AddBlenderModeMappings(
data,
PixelAlphaCompositionMode.SrcAtop,
typeof(DefaultPixelBlenders<Rgba32>.NormalSrcAtop),
typeof(DefaultPixelBlenders<Rgba32>.MultiplySrcAtop),
typeof(DefaultPixelBlenders<Rgba32>.AddSrcAtop),
typeof(DefaultPixelBlenders<Rgba32>.SubtractSrcAtop),
typeof(DefaultPixelBlenders<Rgba32>.ScreenSrcAtop),
typeof(DefaultPixelBlenders<Rgba32>.DarkenSrcAtop),
typeof(DefaultPixelBlenders<Rgba32>.LightenSrcAtop),
typeof(DefaultPixelBlenders<Rgba32>.OverlaySrcAtop),
typeof(DefaultPixelBlenders<Rgba32>.HardLightSrcAtop));
AddBlenderModeMappings(
data,
PixelAlphaCompositionMode.SrcIn,
typeof(DefaultPixelBlenders<Rgba32>.NormalSrcIn),
typeof(DefaultPixelBlenders<Rgba32>.MultiplySrcIn),
typeof(DefaultPixelBlenders<Rgba32>.AddSrcIn),
typeof(DefaultPixelBlenders<Rgba32>.SubtractSrcIn),
typeof(DefaultPixelBlenders<Rgba32>.ScreenSrcIn),
typeof(DefaultPixelBlenders<Rgba32>.DarkenSrcIn),
typeof(DefaultPixelBlenders<Rgba32>.LightenSrcIn),
typeof(DefaultPixelBlenders<Rgba32>.OverlaySrcIn),
typeof(DefaultPixelBlenders<Rgba32>.HardLightSrcIn));
AddBlenderModeMappings(
data,
PixelAlphaCompositionMode.SrcOut,
typeof(DefaultPixelBlenders<Rgba32>.NormalSrcOut),
typeof(DefaultPixelBlenders<Rgba32>.MultiplySrcOut),
typeof(DefaultPixelBlenders<Rgba32>.AddSrcOut),
typeof(DefaultPixelBlenders<Rgba32>.SubtractSrcOut),
typeof(DefaultPixelBlenders<Rgba32>.ScreenSrcOut),
typeof(DefaultPixelBlenders<Rgba32>.DarkenSrcOut),
typeof(DefaultPixelBlenders<Rgba32>.LightenSrcOut),
typeof(DefaultPixelBlenders<Rgba32>.OverlaySrcOut),
typeof(DefaultPixelBlenders<Rgba32>.HardLightSrcOut));
AddBlenderModeMappings(
data,
PixelAlphaCompositionMode.Dest,
typeof(DefaultPixelBlenders<Rgba32>.NormalDest),
typeof(DefaultPixelBlenders<Rgba32>.MultiplyDest),
typeof(DefaultPixelBlenders<Rgba32>.AddDest),
typeof(DefaultPixelBlenders<Rgba32>.SubtractDest),
typeof(DefaultPixelBlenders<Rgba32>.ScreenDest),
typeof(DefaultPixelBlenders<Rgba32>.DarkenDest),
typeof(DefaultPixelBlenders<Rgba32>.LightenDest),
typeof(DefaultPixelBlenders<Rgba32>.OverlayDest),
typeof(DefaultPixelBlenders<Rgba32>.HardLightDest));
AddBlenderModeMappings(
data,
PixelAlphaCompositionMode.DestAtop,
typeof(DefaultPixelBlenders<Rgba32>.NormalDestAtop),
typeof(DefaultPixelBlenders<Rgba32>.MultiplyDestAtop),
typeof(DefaultPixelBlenders<Rgba32>.AddDestAtop),
typeof(DefaultPixelBlenders<Rgba32>.SubtractDestAtop),
typeof(DefaultPixelBlenders<Rgba32>.ScreenDestAtop),
typeof(DefaultPixelBlenders<Rgba32>.DarkenDestAtop),
typeof(DefaultPixelBlenders<Rgba32>.LightenDestAtop),
typeof(DefaultPixelBlenders<Rgba32>.OverlayDestAtop),
typeof(DefaultPixelBlenders<Rgba32>.HardLightDestAtop));
AddBlenderModeMappings(
data,
PixelAlphaCompositionMode.DestIn,
typeof(DefaultPixelBlenders<Rgba32>.NormalDestIn),
typeof(DefaultPixelBlenders<Rgba32>.MultiplyDestIn),
typeof(DefaultPixelBlenders<Rgba32>.AddDestIn),
typeof(DefaultPixelBlenders<Rgba32>.SubtractDestIn),
typeof(DefaultPixelBlenders<Rgba32>.ScreenDestIn),
typeof(DefaultPixelBlenders<Rgba32>.DarkenDestIn),
typeof(DefaultPixelBlenders<Rgba32>.LightenDestIn),
typeof(DefaultPixelBlenders<Rgba32>.OverlayDestIn),
typeof(DefaultPixelBlenders<Rgba32>.HardLightDestIn));
AddBlenderModeMappings(
data,
PixelAlphaCompositionMode.DestOut,
typeof(DefaultPixelBlenders<Rgba32>.NormalDestOut),
typeof(DefaultPixelBlenders<Rgba32>.MultiplyDestOut),
typeof(DefaultPixelBlenders<Rgba32>.AddDestOut),
typeof(DefaultPixelBlenders<Rgba32>.SubtractDestOut),
typeof(DefaultPixelBlenders<Rgba32>.ScreenDestOut),
typeof(DefaultPixelBlenders<Rgba32>.DarkenDestOut),
typeof(DefaultPixelBlenders<Rgba32>.LightenDestOut),
typeof(DefaultPixelBlenders<Rgba32>.OverlayDestOut),
typeof(DefaultPixelBlenders<Rgba32>.HardLightDestOut));
AddBlenderModeMappings(
data,
PixelAlphaCompositionMode.DestOver,
typeof(DefaultPixelBlenders<Rgba32>.NormalDestOver),
typeof(DefaultPixelBlenders<Rgba32>.MultiplyDestOver),
typeof(DefaultPixelBlenders<Rgba32>.AddDestOver),
typeof(DefaultPixelBlenders<Rgba32>.SubtractDestOver),
typeof(DefaultPixelBlenders<Rgba32>.ScreenDestOver),
typeof(DefaultPixelBlenders<Rgba32>.DarkenDestOver),
typeof(DefaultPixelBlenders<Rgba32>.LightenDestOver),
typeof(DefaultPixelBlenders<Rgba32>.OverlayDestOver),
typeof(DefaultPixelBlenders<Rgba32>.HardLightDestOver));
AddBlenderModeMappings(
data,
PixelAlphaCompositionMode.SrcOver,
typeof(DefaultPixelBlenders<Rgba32>.NormalSrcOver),
typeof(DefaultPixelBlenders<Rgba32>.MultiplySrcOver),
typeof(DefaultPixelBlenders<Rgba32>.AddSrcOver),
typeof(DefaultPixelBlenders<Rgba32>.SubtractSrcOver),
typeof(DefaultPixelBlenders<Rgba32>.ScreenSrcOver),
typeof(DefaultPixelBlenders<Rgba32>.DarkenSrcOver),
typeof(DefaultPixelBlenders<Rgba32>.LightenSrcOver),
typeof(DefaultPixelBlenders<Rgba32>.OverlaySrcOver),
typeof(DefaultPixelBlenders<Rgba32>.HardLightSrcOver));
return data;
}
}
[Theory]
[MemberData(nameof(BlenderMappings))]
public void ReturnsCorrectBlender<TPixel>(TestPixel<TPixel> pixel, Type type, PixelColorBlendingMode mode)
@ -41,6 +208,126 @@ public class PixelBlenderTests
Assert.IsType(type, blender);
}
[Theory]
[MemberData(nameof(BlenderModeMappings))]
public void ReturnsCorrectBlenderForAllModeCombinations(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode, Type type)
{
PixelBlender<Rgba32> blender = PixelOperations<Rgba32>.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<Rgba32> blender = new DefaultPixelBlenders<Rgba32>.NormalSrcOver();
Rgba32[] destination = new Rgba32[2];
Rgba32[] background =
[
Color.Red.ToPixel<Rgba32>(),
Color.Green.ToPixel<Rgba32>()
];
Rgba32 source = Color.Blue.ToPixel<Rgba32>();
blender.Blend(Configuration.Default, destination, background, source, 1F);
Assert.Equal(source, destination[0]);
Assert.Equal(source, destination[1]);
}
[Fact]
public void Blend_WithConstantSourceSingleAmountAndWorkingBuffer()
{
PixelBlender<Rgba32> blender = new DefaultPixelBlenders<Rgba32>.NormalSrcOver();
Rgba32[] destination = new Rgba32[2];
Rgba32[] background =
[
Color.Red.ToPixel<Rgba32>(),
Color.Green.ToPixel<Rgba32>()
];
Rgba32 source = Color.Blue.ToPixel<Rgba32>();
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<Rgba32> blender = new DefaultPixelBlenders<Rgba32>.NormalSrcOver();
Rgba32[] destination = new Rgba32[2];
Rgba32[] background =
[
Color.Red.ToPixel<Rgba32>(),
Color.Green.ToPixel<Rgba32>()
];
Rgba32 source = Color.Blue.ToPixel<Rgba32>();
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<Rgba32> blender = new DefaultPixelBlenders<Rgba32>.NormalSrcOver();
Rgba32[] destination = new Rgba32[2];
Rgba32[] background =
[
Color.Red.ToPixel<Rgba32>(),
Color.Green.ToPixel<Rgba32>()
];
Rgba32 source = Color.Blue.ToPixel<Rgba32>();
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<Rgba32> blender = new DefaultPixelBlenders<Rgba32>.NormalSrcOver();
Rgba32[] destination = new Rgba32[2];
Rgba32[] background =
[
Color.Red.ToPixel<Rgba32>(),
Color.Green.ToPixel<Rgba32>()
];
Rgba32[] source =
[
Color.Blue.ToPixel<Rgba32>(),
Color.Yellow.ToPixel<Rgba32>()
];
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<Rgba32, Rgba32, float, PixelColorBlendingMode, Rgba32> ColorBlendingExpectedResults = new()
{
{ Color.MistyRose.ToPixel<Rgba32>(), Color.MidnightBlue.ToPixel<Rgba32>(), 1, PixelColorBlendingMode.Normal, Color.MidnightBlue.ToPixel<Rgba32>() },
@ -92,4 +379,67 @@ public class PixelBlenderTests
// var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults
Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4());
}
private static void AddBlenderModeMappings(
TheoryData<PixelColorBlendingMode, PixelAlphaCompositionMode, Type> 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<PixelAlphaCompositionMode>())
{
foreach (PixelColorBlendingMode colorMode in Enum.GetValues<PixelColorBlendingMode>())
{
PixelBlender<Rgba32> blender = PixelOperations<Rgba32>.Instance.GetPixelBlender(colorMode, alphaMode);
ExerciseBlender(blender);
}
}
}
private static void ExerciseBlender(PixelBlender<Rgba32> blender)
{
Rgba32 background = Color.MistyRose.ToPixel<Rgba32>();
Rgba32 source = Color.MidnightBlue.ToPixel<Rgba32>();
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<Rgba32>(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));
}
}

2
tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs

@ -59,7 +59,7 @@ public class PorterDuffCompositorTests
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX,
provider,
mode.ToString());
}

2
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,

2
tests/coverlet.runsettings

@ -9,7 +9,7 @@
<DataCollector friendlyName="XPlat code coverage">
<Configuration>
<Format>lcov</Format>
<Include>[SixLabors.*]*</Include>
<Include>[SixLabors.ImageSharp*]*</Include>
<UseSourceLink>true</UseSourceLink>
</Configuration>
</DataCollector>

Loading…
Cancel
Save