Browse Source

entry API & tests

js/color-alpha-handling
Anton Firszov 5 years ago
parent
commit
dd0447ef4c
  1. 21
      src/ImageSharp/Common/Helpers/SimdUtils.cs
  2. 26
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
  3. 105
      tests/ImageSharp.Tests/Common/SimdUtilsTests.cs
  4. 18
      tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs

21
src/ImageSharp/Common/Helpers/SimdUtils.cs

@ -6,6 +6,7 @@ using System.Diagnostics;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
#if SUPPORTS_RUNTIME_INTRINSICS #if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
@ -147,6 +148,26 @@ namespace SixLabors.ImageSharp
} }
} }
[MethodImpl(InliningOptions.ShortMethod)]
internal static void PackFromRgbPlanes(
Configuration configuration,
ReadOnlySpan<byte> redChannel,
ReadOnlySpan<byte> greenChannel,
ReadOnlySpan<byte> blueChannel,
Span<Rgb24> destination)
{
}
[MethodImpl(InliningOptions.ShortMethod)]
internal static void PackFromRgbPlanes(
Configuration configuration,
ReadOnlySpan<byte> redChannel,
ReadOnlySpan<byte> greenChannel,
ReadOnlySpan<byte> blueChannel,
Span<Rgba32> destination)
{
}
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
private static void ConvertByteToNormalizedFloatRemainder(ReadOnlySpan<byte> source, Span<float> dest) private static void ConvertByteToNormalizedFloatRemainder(ReadOnlySpan<byte> source, Span<float> dest)
{ {

26
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

@ -159,5 +159,31 @@ namespace SixLabors.ImageSharp.PixelFormats
PixelOperations<TDestinationPixel>.Instance.From(configuration, sourcePixels, destinationPixels); PixelOperations<TDestinationPixel>.Instance.From(configuration, sourcePixels, destinationPixels);
} }
/// <summary>
/// Bulk operation that converts 3 seperate RGB channels to <paramref name="destination"/>
/// </summary>
/// <param name="configuration">A <see cref="Configuration"/> to configure internal operations.</param>
/// <param name="redChannel">A <see cref="ReadOnlySpan{T}"/> to the red values.</param>
/// <param name="greenChannel">A <see cref="ReadOnlySpan{T}"/> to the green values.</param>
/// <param name="blueChannel">A <see cref="ReadOnlySpan{T}"/> to the blue values.</param>
/// <param name="destination">A <see cref="Span{T}"/> to the destination pixels.</param>
public virtual void PackFromRgbPlanes(
Configuration configuration,
ReadOnlySpan<byte> redChannel,
ReadOnlySpan<byte> greenChannel,
ReadOnlySpan<byte> blueChannel,
Span<TPixel> 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);
}
}
} }
} }

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

@ -7,6 +7,7 @@ using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Common.Tuples; using SixLabors.ImageSharp.Common.Tuples;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
@ -336,90 +337,64 @@ namespace SixLabors.ImageSharp.Tests.Common
} }
} }
private static void TestImpl_BulkConvertNormalizedFloatToByteClampOverflows( [Theory]
int count, [MemberData(nameof(ArbitraryArraySizes))]
Action<Memory<float>, public void PackFromRgbPlanes_Rgb24(int count)
Memory<byte>> convert,
int seed = -1)
{ {
seed = seed > 0 ? seed : count; TestPackFromRgbPlanes<Rgb24>(
float[] source = new Random(seed).GenerateRandomFloatArray(count, -0.2f, 1.2f); count,
byte[] expected = source.Select(NormalizedFloatToByte).ToArray(); (r, g, b, actual) =>
var actual = new byte[count]; SimdUtils.PackFromRgbPlanes(Configuration.Default, r, g, b, actual));
convert(source, actual);
Assert.Equal(expected, actual);
} }
private static byte NormalizedFloatToByte(float f) => (byte)Math.Min(255f, Math.Max(0f, (f * 255f) + 0.5f));
[Theory] [Theory]
[InlineData(0)] [MemberData(nameof(ArbitraryArraySizes))]
[InlineData(7)] public void PackFromRgbPlanes_Rgba32(int count)
[InlineData(42)]
[InlineData(255)]
[InlineData(256)]
[InlineData(257)]
private void MagicConvertToByte(float value)
{ {
byte actual = MagicConvert(value / 256f); TestPackFromRgbPlanes<Rgba32>(
var expected = (byte)value; count,
(r, g, b, actual) =>
Assert.Equal(expected, actual); SimdUtils.PackFromRgbPlanes(Configuration.Default, r, g, b, actual));
} }
[Fact] internal static void TestPackFromRgbPlanes<TPixel>(int count, Action<byte[], byte[], byte[], TPixel[]> packMethod)
private void BulkConvertNormalizedFloatToByte_Step() where TPixel : unmanaged, IPixel<TPixel>
{ {
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 }; TPixel[] actual = new TPixel[count];
packMethod(r, g, b, actual);
byte[] expected = source.Select(f => (byte)Math.Round(f)).ToArray();
source = source.Select(f => f / 255f).ToArray(); Assert.Equal(expected, actual);
Span<byte> 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<float, uint>(ref f);
return (byte)i;
} }
private void MagicConvert(Span<float> source, Span<byte> dest) private static void TestImpl_BulkConvertNormalizedFloatToByteClampOverflows(
int count,
Action<Memory<float>,
Memory<byte>> convert,
int seed = -1)
{ {
var magick = new Vector<float>(32768.0f); seed = seed > 0 ? seed : count;
float[] source = new Random(seed).GenerateRandomFloatArray(count, -0.2f, 1.2f);
var scale = new Vector<float>(255f) / new Vector<float>(256f); byte[] expected = source.Select(NormalizedFloatToByte).ToArray();
var actual = new byte[count];
Vector<float> x = MemoryMarshal.Cast<float, Vector<float>>(source)[0];
x = (x * scale) + magick;
Tuple8.OfUInt32 ii = default;
ref Vector<float> iiRef = ref Unsafe.As<Tuple8.OfUInt32, Vector<float>>(ref ii);
iiRef = x;
ref Tuple8.OfByte d = ref MemoryMarshal.Cast<byte, Tuple8.OfByte>(dest)[0]; convert(source, actual);
d.LoadFrom(ref ii);
this.Output.WriteLine(ii.ToString()); Assert.Equal(expected, actual);
this.Output.WriteLine(d.ToString());
} }
private static byte NormalizedFloatToByte(float f) => (byte)Math.Min(255f, Math.Max(0f, (f * 255f) + 0.5f));
private static void AssertEvenRoundIsCorrect(Vector<float> r, Vector<float> v) private static void AssertEvenRoundIsCorrect(Vector<float> r, Vector<float> v)
{ {
for (int i = 0; i < Vector<float>.Count; i++) for (int i = 0; i < Vector<float>.Count; i++)

18
tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs

@ -10,6 +10,7 @@ using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Companding; using SixLabors.ImageSharp.ColorSpaces.Companding;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.Common;
using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
@ -1002,6 +1003,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
(s, d) => this.Operations.ToRgba64Bytes(this.Configuration, s, d.GetSpan(), count)); (s, d) => this.Operations.ToRgba64Bytes(this.Configuration, s, d.GetSpan(), count));
} }
[Theory]
[MemberData(nameof(ArraySizesData))]
public void PackFromRgbPlanes(int count)
{
SimdUtilsTests.TestPackFromRgbPlanes<TPixel>(
count,
(
r,
g,
b,
actual) => PixelOperations<TPixel>.Instance.PackFromRgbPlanes(this.Configuration, r, g, b, actual));
}
public delegate void RefAction<T1>(ref T1 arg1); public delegate void RefAction<T1>(ref T1 arg1);
internal static Vector4[] CreateExpectedVector4Data(TPixel[] source, RefAction<Vector4> vectorModifier = null) internal static Vector4[] CreateExpectedVector4Data(TPixel[] source, RefAction<Vector4> vectorModifier = null)
@ -1102,10 +1116,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
return result; return result;
} }
internal static byte[] CreateByteTestData(int length) internal static byte[] CreateByteTestData(int length, int seed = 42)
{ {
byte[] result = new byte[length]; 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++) for (int i = 0; i < result.Length; i++)
{ {

Loading…
Cancel
Save