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
};
public static ReadOnlySpan<byte> PermuteMaskShiftAlpha8x32 =>
public static ReadOnlySpan<byte> PermuteMaskShiftAlpha8x32 =>
new byte[]
{
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);
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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -20,12 +23,9 @@ namespace SixLabors.ImageSharp
ReadOnlySpan<byte> blueChannel,
Span<Rgb24> destination)
{
int count = redChannel.Length;
DebugGuard.IsTrue(greenChannel.Length == count, "Channels must be of same size!");
DebugGuard.IsTrue(blueChannel.Length == count, "Channels must be of same size!");
// 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!");
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 + 2, nameof(destination), "'destination' must contain a padding of 3 elements!");
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
@ -49,7 +49,21 @@ namespace SixLabors.ImageSharp
ReadOnlySpan<byte> blueChannel,
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);
}

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

@ -21,6 +21,22 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
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<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
[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> gBase = ref Unsafe.As<float, Vector256<float>>(ref this.gFloat[0]);
@ -237,7 +237,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion
}
[Benchmark]
public void Rgba32_Vector_Bytes()
public void Rgba24_Avx2_Bytes()
{
ReadOnlySpan<byte> r = this.rBuf;
ReadOnlySpan<byte> g = this.rBuf;
@ -245,6 +245,16 @@ namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion
Span<Rgb24> rgb = this.rgbBuf;
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
#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(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
internal static void TestPackFromRgbPlanes<TPixel>(int count, Action<byte[], byte[], byte[], TPixel[]> packMethod)

Loading…
Cancel
Save