// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Numerics; using SixLabors.ImageSharp.PixelFormats.Utils; using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.PixelFormats; /// /// Verifies the shared stateful traversal used by affine pixel-vector conversions. /// [Trait("Category", "PixelFormats")] public class Vector4ConvertersTests { private static readonly int[] Lengths = [0, 1, 2, 3, 4, 5, 7, 8, 15, 16, 17, 257]; /// /// Verifies multiply-then-add behavior for every SIMD boundary and the software fallback. /// [Fact] public void MultiplyThenAddMatchesComponentArithmeticAcrossHardwareWidths() => FeatureTestRunner.RunWithHwIntrinsicsFeature(AssertMultiplyThenAddMatchesComponentArithmetic, HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); /// /// Verifies add-then-divide behavior for every SIMD boundary and the software fallback. /// [Fact] public void AddThenDivideMatchesComponentArithmeticAcrossHardwareWidths() => FeatureTestRunner.RunWithHwIntrinsicsFeature(AssertAddThenDivideMatchesComponentArithmetic, HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); /// /// Compares the multiply-then-add traversal with independently evaluated component expressions. /// private static void AssertMultiplyThenAddMatchesComponentArithmetic() { Vector4 multiplier = new(2F, -3F, .5F, 4F); Vector4 offset = new(-7F, 11F, 13F, -17F); foreach (int length in Lengths) { Vector4[] actual = CreateSource(length); Vector4[] expected = new Vector4[length]; for (int i = 0; i < expected.Length; i++) { Vector4 value = actual[i]; expected[i] = new Vector4((value.X * multiplier.X) + offset.X, (value.Y * multiplier.Y) + offset.Y, (value.Z * multiplier.Z) + offset.Z, (value.W * multiplier.W) + offset.W); } Vector4Converters.MultiplyThenAdd(actual, multiplier, offset); Assert.Equal(expected, actual); } } /// /// Compares the add-then-divide traversal with independently evaluated component expressions. /// private static void AssertAddThenDivideMatchesComponentArithmetic() { Vector4 offset = new(-7F, 11F, 13F, -17F); Vector4 divisor = new(2F, -3F, .5F, 4F); foreach (int length in Lengths) { Vector4[] actual = CreateSource(length); Vector4[] expected = new Vector4[length]; for (int i = 0; i < expected.Length; i++) { Vector4 value = actual[i]; expected[i] = new Vector4((value.X + offset.X) / divisor.X, (value.Y + offset.Y) / divisor.Y, (value.Z + offset.Z) / divisor.Z, (value.W + offset.W) / divisor.W); } Vector4Converters.AddThenDivide(actual, offset, divisor); Assert.Equal(expected, actual); } } /// /// Creates non-uniform values that expose component ordering and traversal overlap errors. /// /// The number of vectors to create. /// The populated vector buffer. private static Vector4[] CreateSource(int length) { Vector4[] result = new Vector4[length]; for (int i = 0; i < result.Length; i++) { result[i] = new Vector4((i * 17F) - 31F, (i * -23F) + 37F, (i * .25F) - 41F, (i * 3F) + 43F); } return result; } }