Browse Source

polishing

js/color-alpha-handling
Anton Firszov 5 years ago
parent
commit
2ff0cb93dc
  1. 61
      src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
  2. 28
      src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs
  3. 16
      src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs
  4. 16
      src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs
  5. 14
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs
  6. 35
      tests/ImageSharp.Tests/Common/SimdUtilsTests.cs

61
src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs

@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp
0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15
}; };
public static ReadOnlySpan<byte> PermuteMaskShiftAlpha8x32 => public static ReadOnlySpan<byte> PermuteMaskShiftAlpha8x32 =>
new byte[] new byte[]
{ {
0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0,
@ -877,6 +877,65 @@ namespace SixLabors.ImageSharp
blueChannel = blueChannel.Slice(slice); blueChannel = blueChannel.Slice(slice);
destination = destination.Slice(slice); destination = destination.Slice(slice);
} }
internal static void PackFromRgbPlanesAvx2Reduce(
ref ReadOnlySpan<byte> redChannel,
ref ReadOnlySpan<byte> greenChannel,
ref ReadOnlySpan<byte> blueChannel,
ref Span<Rgba32> destination)
{
ref Vector256<byte> rBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(redChannel));
ref Vector256<byte> gBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(greenChannel));
ref Vector256<byte> bBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(blueChannel));
ref Vector256<byte> dBase = ref Unsafe.As<Rgba32, Vector256<byte>>(ref MemoryMarshal.GetReference(destination));
int count = redChannel.Length / Vector256<byte>.Count;
ref byte control1Bytes = ref MemoryMarshal.GetReference(SimdUtils.HwIntrinsics.PermuteMaskEvenOdd8x32);
Vector256<uint> control1 = Unsafe.As<byte, Vector256<uint>>(ref control1Bytes);
ref byte control2Bytes = ref MemoryMarshal.GetReference(PermuteMaskShiftAlpha8x32);
Vector256<uint> control2 = Unsafe.As<byte, Vector256<uint>>(ref control2Bytes);
Vector256<byte> a = Vector256.Create((byte)255);
Vector256<byte> shuffleAlpha = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ShuffleMaskShiftAlpha));
for (int i = 0; i < count; i++)
{
Vector256<byte> r0 = Unsafe.Add(ref rBase, i);
Vector256<byte> g0 = Unsafe.Add(ref gBase, i);
Vector256<byte> b0 = Unsafe.Add(ref bBase, i);
r0 = Avx2.PermuteVar8x32(r0.AsUInt32(), control1).AsByte();
g0 = Avx2.PermuteVar8x32(g0.AsUInt32(), control1).AsByte();
b0 = Avx2.PermuteVar8x32(b0.AsUInt32(), control1).AsByte();
Vector256<byte> rg = Avx2.UnpackLow(r0, g0);
Vector256<byte> b1 = Avx2.UnpackLow(b0, a);
Vector256<byte> rgb1 = Avx2.UnpackLow(rg.AsUInt16(), b1.AsUInt16()).AsByte();
Vector256<byte> rgb2 = Avx2.UnpackHigh(rg.AsUInt16(), b1.AsUInt16()).AsByte();
rg = Avx2.UnpackHigh(r0, g0);
b1 = Avx2.UnpackHigh(b0, a);
Vector256<byte> rgb3 = Avx2.UnpackLow(rg.AsUInt16(), b1.AsUInt16()).AsByte();
Vector256<byte> rgb4 = Avx2.UnpackHigh(rg.AsUInt16(), b1.AsUInt16()).AsByte();
ref Vector256<byte> d0 = ref Unsafe.Add(ref dBase, i * 4);
d0 = rgb1;
Unsafe.Add(ref d0, 1) = rgb2;
Unsafe.Add(ref d0, 2) = rgb3;
Unsafe.Add(ref d0, 3) = rgb4;
}
int slice = count * Vector256<byte>.Count;
redChannel = redChannel.Slice(slice);
greenChannel = greenChannel.Slice(slice);
blueChannel = blueChannel.Slice(slice);
destination = destination.Slice(slice);
}
} }
} }
} }

28
src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs

@ -1,3 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -20,12 +23,9 @@ namespace SixLabors.ImageSharp
ReadOnlySpan<byte> blueChannel, ReadOnlySpan<byte> blueChannel,
Span<Rgb24> destination) Span<Rgb24> destination)
{ {
int count = redChannel.Length; DebugGuard.IsTrue(greenChannel.Length == redChannel.Length, nameof(greenChannel), "Channels must be of same size!");
DebugGuard.IsTrue(greenChannel.Length == count, "Channels must be of same size!"); DebugGuard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!");
DebugGuard.IsTrue(blueChannel.Length == count, "Channels must be of same size!"); DebugGuard.IsTrue(destination.Length > redChannel.Length + 2, nameof(destination), "'destination' must contain a padding of 3 elements!");
// To avoid overflows, this check is not debug-only:
Guard.IsTrue(destination.Length > count + 2, nameof(destination), "'destination' must contain a padding of 3 elements!");
#if SUPPORTS_RUNTIME_INTRINSICS #if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
@ -49,7 +49,21 @@ namespace SixLabors.ImageSharp
ReadOnlySpan<byte> blueChannel, ReadOnlySpan<byte> blueChannel,
Span<Rgba32> destination) Span<Rgba32> destination)
{ {
PackFromRgbPlanesScalarBatchedReduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination); DebugGuard.IsTrue(greenChannel.Length == redChannel.Length, nameof(greenChannel), "Channels must be of same size!");
DebugGuard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!");
DebugGuard.IsTrue(destination.Length > redChannel.Length, nameof(destination), "'destination' span should not be shorter than the source channels!");
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination);
}
else
#endif
{
PackFromRgbPlanesScalarBatchedReduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination);
}
PackFromRgbPlanesRemainder(redChannel, greenChannel, blueChannel, destination); PackFromRgbPlanesRemainder(redChannel, greenChannel, blueChannel, destination);
} }

16
src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs

@ -21,6 +21,22 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value;
/// <inheritdoc />
internal override void PackFromRgbPlanes(
Configuration configuration,
ReadOnlySpan<byte> redChannel,
ReadOnlySpan<byte> greenChannel,
ReadOnlySpan<byte> blueChannel,
Span<Rgb24> destination)
{
int count = redChannel.Length;
Guard.IsTrue(greenChannel.Length == count, nameof(greenChannel), "Channels must be of same size!");
Guard.IsTrue(blueChannel.Length == count, nameof(blueChannel), "Channels must be of same size!");
Guard.IsTrue(destination.Length > count + 2, nameof(destination), "'destination' must contain a padding of 3 elements!");
SimdUtils.PackFromRgbPlanes(configuration, redChannel, greenChannel, blueChannel, destination);
}
} }
} }
} }

16
src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs

@ -56,6 +56,22 @@ namespace SixLabors.ImageSharp.PixelFormats
MemoryMarshal.Cast<Vector4, float>(sourceVectors), MemoryMarshal.Cast<Vector4, float>(sourceVectors),
MemoryMarshal.Cast<Rgba32, byte>(destinationPixels)); MemoryMarshal.Cast<Rgba32, byte>(destinationPixels));
} }
/// <inheritdoc />
internal override void PackFromRgbPlanes(
Configuration configuration,
ReadOnlySpan<byte> redChannel,
ReadOnlySpan<byte> greenChannel,
ReadOnlySpan<byte> blueChannel,
Span<Rgba32> destination)
{
int count = redChannel.Length;
Guard.IsTrue(greenChannel.Length == count, nameof(greenChannel), "Channels must be of same size!");
Guard.IsTrue(blueChannel.Length == count, nameof(blueChannel), "Channels must be of same size!");
Guard.IsTrue(destination.Length > count, nameof(destination), "'destination' span should not be shorter than the source channels!");
SimdUtils.PackFromRgbPlanes(configuration, redChannel, greenChannel, blueChannel, destination);
}
} }
} }
} }

14
tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs

@ -196,7 +196,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion
#if SUPPORTS_RUNTIME_INTRINSICS #if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void Rgba32_Vector_Float() public void Rgba32_Avx2_Float()
{ {
ref Vector256<float> rBase = ref Unsafe.As<float, Vector256<float>>(ref this.rFloat[0]); ref Vector256<float> rBase = ref Unsafe.As<float, Vector256<float>>(ref this.rFloat[0]);
ref Vector256<float> gBase = ref Unsafe.As<float, Vector256<float>>(ref this.gFloat[0]); ref Vector256<float> gBase = ref Unsafe.As<float, Vector256<float>>(ref this.gFloat[0]);
@ -237,7 +237,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion
} }
[Benchmark] [Benchmark]
public void Rgba32_Vector_Bytes() public void Rgba24_Avx2_Bytes()
{ {
ReadOnlySpan<byte> r = this.rBuf; ReadOnlySpan<byte> r = this.rBuf;
ReadOnlySpan<byte> g = this.rBuf; ReadOnlySpan<byte> g = this.rBuf;
@ -245,6 +245,16 @@ namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion
Span<Rgb24> rgb = this.rgbBuf; Span<Rgb24> rgb = this.rgbBuf;
SimdUtils.HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref r, ref g, ref b, ref rgb); SimdUtils.HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref r, ref g, ref b, ref rgb);
} }
[Benchmark]
public void Rgba32_Avx2_Bytes()
{
ReadOnlySpan<byte> r = this.rBuf;
ReadOnlySpan<byte> g = this.rBuf;
ReadOnlySpan<byte> b = this.rBuf;
Span<Rgba32> rgb = this.rgbaBuf;
SimdUtils.HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref r, ref g, ref b, ref rgb);
}
#endif #endif
#pragma warning disable SA1132 #pragma warning disable SA1132

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

@ -392,6 +392,41 @@ namespace SixLabors.ImageSharp.Tests.Common
Assert.Equal(0, bb.Length); Assert.Equal(0, bb.Length);
Assert.Equal(padding, dd.Length); Assert.Equal(padding, dd.Length);
} }
[Fact]
public void PackFromRgbPlanesAvx2Reduce_Rgba32()
{
if (!Avx2.IsSupported)
{
return;
}
byte[] r = Enumerable.Range(0, 32).Select(x => (byte)x).ToArray();
byte[] g = Enumerable.Range(100, 32).Select(x => (byte)x).ToArray();
byte[] b = Enumerable.Range(200, 32).Select(x => (byte)x).ToArray();
Rgba32[] d = new Rgba32[32];
ReadOnlySpan<byte> rr = r.AsSpan();
ReadOnlySpan<byte> gg = g.AsSpan();
ReadOnlySpan<byte> bb = b.AsSpan();
Span<Rgba32> dd = d.AsSpan();
SimdUtils.HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref rr, ref gg, ref bb, ref dd);
for (int i = 0; i < 32; i++)
{
Assert.Equal(i, d[i].R);
Assert.Equal(i + 100, d[i].G);
Assert.Equal(i + 200, d[i].B);
Assert.Equal(255, d[i].A);
}
Assert.Equal(0, rr.Length);
Assert.Equal(0, gg.Length);
Assert.Equal(0, bb.Length);
Assert.Equal(0, dd.Length);
}
#endif #endif
internal static void TestPackFromRgbPlanes<TPixel>(int count, Action<byte[], byte[], byte[], TPixel[]> packMethod) internal static void TestPackFromRgbPlanes<TPixel>(int count, Action<byte[], byte[], byte[], TPixel[]> packMethod)

Loading…
Cancel
Save