From dd0447ef4cb26d12c7e7438eaecde588b156aacc Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 5 Dec 2020 16:30:31 +0100 Subject: [PATCH] entry API & tests --- src/ImageSharp/Common/Helpers/SimdUtils.cs | 21 ++++ .../PixelFormats/PixelOperations{TPixel}.cs | 26 +++++ .../ImageSharp.Tests/Common/SimdUtilsTests.cs | 105 +++++++----------- .../PixelOperations/PixelOperationsTests.cs | 18 ++- 4 files changed, 103 insertions(+), 67 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs index aaf6d405c..f37226a1a 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats; #if SUPPORTS_RUNTIME_INTRINSICS using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; @@ -147,6 +148,26 @@ namespace SixLabors.ImageSharp } } + [MethodImpl(InliningOptions.ShortMethod)] + internal static void PackFromRgbPlanes( + Configuration configuration, + ReadOnlySpan redChannel, + ReadOnlySpan greenChannel, + ReadOnlySpan blueChannel, + Span destination) + { + } + + [MethodImpl(InliningOptions.ShortMethod)] + internal static void PackFromRgbPlanes( + Configuration configuration, + ReadOnlySpan redChannel, + ReadOnlySpan greenChannel, + ReadOnlySpan blueChannel, + Span destination) + { + } + [MethodImpl(InliningOptions.ColdPath)] private static void ConvertByteToNormalizedFloatRemainder(ReadOnlySpan source, Span dest) { diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index dbe06702d..e562f333c 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -159,5 +159,31 @@ namespace SixLabors.ImageSharp.PixelFormats PixelOperations.Instance.From(configuration, sourcePixels, destinationPixels); } + + /// + /// Bulk operation that converts 3 seperate RGB channels to + /// + /// A to configure internal operations. + /// A to the red values. + /// A to the green values. + /// A to the blue values. + /// A to the destination pixels. + public virtual void PackFromRgbPlanes( + Configuration configuration, + ReadOnlySpan redChannel, + ReadOnlySpan greenChannel, + ReadOnlySpan blueChannel, + Span destination) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(redChannel, destination, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + var rgb24 = new Rgb24(redChannel[i], greenChannel[i], blueChannel[i]); + + destination[i].FromRgb24(rgb24); + } + } } } diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index ec09e43e5..38b4e2e8c 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -7,6 +7,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Tuples; +using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; using Xunit.Abstractions; @@ -336,90 +337,64 @@ namespace SixLabors.ImageSharp.Tests.Common } } - private static void TestImpl_BulkConvertNormalizedFloatToByteClampOverflows( - int count, - Action, - Memory> convert, - int seed = -1) + [Theory] + [MemberData(nameof(ArbitraryArraySizes))] + public void PackFromRgbPlanes_Rgb24(int count) { - seed = seed > 0 ? seed : count; - float[] source = new Random(seed).GenerateRandomFloatArray(count, -0.2f, 1.2f); - byte[] expected = source.Select(NormalizedFloatToByte).ToArray(); - var actual = new byte[count]; - - convert(source, actual); - - Assert.Equal(expected, actual); + TestPackFromRgbPlanes( + count, + (r, g, b, actual) => + SimdUtils.PackFromRgbPlanes(Configuration.Default, r, g, b, actual)); } - private static byte NormalizedFloatToByte(float f) => (byte)Math.Min(255f, Math.Max(0f, (f * 255f) + 0.5f)); - [Theory] - [InlineData(0)] - [InlineData(7)] - [InlineData(42)] - [InlineData(255)] - [InlineData(256)] - [InlineData(257)] - private void MagicConvertToByte(float value) + [MemberData(nameof(ArbitraryArraySizes))] + public void PackFromRgbPlanes_Rgba32(int count) { - byte actual = MagicConvert(value / 256f); - var expected = (byte)value; - - Assert.Equal(expected, actual); + TestPackFromRgbPlanes( + count, + (r, g, b, actual) => + SimdUtils.PackFromRgbPlanes(Configuration.Default, r, g, b, actual)); } - [Fact] - private void BulkConvertNormalizedFloatToByte_Step() + internal static void TestPackFromRgbPlanes(int count, Action packMethod) + where TPixel : unmanaged, IPixel { - if (this.SkipOnNonAvx2()) + Random rnd = new Random(42); + byte[] r = rnd.GenerateRandomByteArray(count); + byte[] g = rnd.GenerateRandomByteArray(count); + byte[] b = rnd.GenerateRandomByteArray(count); + + TPixel[] expected = new TPixel[count]; + for (int i = 0; i < count; i++) { - return; + expected[i].FromRgb24(new Rgb24(r[i], g[i], b[i])); } - float[] source = { 0, 7, 42, 255, 0.5f, 1.1f, 2.6f, 16f }; - - byte[] expected = source.Select(f => (byte)Math.Round(f)).ToArray(); + TPixel[] actual = new TPixel[count]; + packMethod(r, g, b, actual); - source = source.Select(f => f / 255f).ToArray(); - - Span dest = stackalloc byte[8]; - - this.MagicConvert(source, dest); - - Assert.True(dest.SequenceEqual(expected)); - } - - private static byte MagicConvert(float x) - { - float f = 32768.0f + x; - uint i = Unsafe.As(ref f); - return (byte)i; + Assert.Equal(expected, actual); } - private void MagicConvert(Span source, Span dest) + private static void TestImpl_BulkConvertNormalizedFloatToByteClampOverflows( + int count, + Action, + Memory> convert, + int seed = -1) { - var magick = new Vector(32768.0f); - - var scale = new Vector(255f) / new Vector(256f); - - Vector x = MemoryMarshal.Cast>(source)[0]; - - x = (x * scale) + magick; - - Tuple8.OfUInt32 ii = default; - - ref Vector iiRef = ref Unsafe.As>(ref ii); - - iiRef = x; + seed = seed > 0 ? seed : count; + float[] source = new Random(seed).GenerateRandomFloatArray(count, -0.2f, 1.2f); + byte[] expected = source.Select(NormalizedFloatToByte).ToArray(); + var actual = new byte[count]; - ref Tuple8.OfByte d = ref MemoryMarshal.Cast(dest)[0]; - d.LoadFrom(ref ii); + convert(source, actual); - this.Output.WriteLine(ii.ToString()); - this.Output.WriteLine(d.ToString()); + Assert.Equal(expected, actual); } + private static byte NormalizedFloatToByte(float f) => (byte)Math.Min(255f, Math.Max(0f, (f * 255f) + 0.5f)); + private static void AssertEvenRoundIsCorrect(Vector r, Vector v) { for (int i = 0; i < Vector.Count; i++) diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index 8d74ccec4..39786a217 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -10,6 +10,7 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.ColorSpaces.Companding; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Common; using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; using Xunit.Abstractions; @@ -1002,6 +1003,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations (s, d) => this.Operations.ToRgba64Bytes(this.Configuration, s, d.GetSpan(), count)); } + [Theory] + [MemberData(nameof(ArraySizesData))] + public void PackFromRgbPlanes(int count) + { + SimdUtilsTests.TestPackFromRgbPlanes( + count, + ( + r, + g, + b, + actual) => PixelOperations.Instance.PackFromRgbPlanes(this.Configuration, r, g, b, actual)); + } + public delegate void RefAction(ref T1 arg1); internal static Vector4[] CreateExpectedVector4Data(TPixel[] source, RefAction vectorModifier = null) @@ -1102,10 +1116,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations return result; } - internal static byte[] CreateByteTestData(int length) + internal static byte[] CreateByteTestData(int length, int seed = 42) { byte[] result = new byte[length]; - var rnd = new Random(42); // Deterministic random values + var rnd = new Random(seed); // Deterministic random values for (int i = 0; i < result.Length; i++) {