Browse Source

Add SSE2 version of TransformColorInverse

pull/1552/head
Brian Popow 5 years ago
parent
commit
cf7a6986f4
  1. 62
      src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs
  2. 30
      tests/ImageSharp.Benchmarks/Codecs/DecodeWebp.cs
  3. 52
      tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs
  4. 2
      tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs

62
src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs

@ -369,7 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{
uint colorCode = transformData[predRowIdx++];
ColorCodeToMultipliers(colorCode, ref m);
TransformColorInverse(m, pixelData, pixelPos, tileWidth);
TransformColorInverse(m, pixelData.Slice(pixelPos, tileWidth));
pixelPos += tileWidth;
}
@ -377,7 +377,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{
uint colorCode = transformData[predRowIdx];
ColorCodeToMultipliers(colorCode, ref m);
TransformColorInverse(m, pixelData, pixelPos, remainingWidth);
TransformColorInverse(m, pixelData.Slice(pixelPos, remainingWidth));
pixelPos += remainingWidth;
}
@ -389,6 +389,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
}
}
/// <summary>
/// Color transform keeps the green (G) value as it is, transforms red (R) based on green and transforms blue (B) based on green and then based on red.
/// </summary>
/// <param name="m">The Vp8LMultipliers.</param>
/// <param name="data">The pixel data to transform.</param>
/// <param name="numPixels">The number of pixels to process.</param>
public static void TransformColor(Vp8LMultipliers m, Span<uint> data, int numPixels)
{
#if SUPPORTS_RUNTIME_INTRINSICS
@ -432,7 +438,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
}
}
public static void TransformColorNoneVectorized(Vp8LMultipliers m, Span<uint> data, int numPixels)
private static void TransformColorNoneVectorized(Vp8LMultipliers m, Span<uint> data, int numPixels)
{
for (int i = 0; i < numPixels; i++)
{
@ -455,12 +461,52 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
/// </summary>
/// <param name="m">The color transform element.</param>
/// <param name="pixelData">The pixel data to apply the inverse transform on.</param>
/// <param name="start">The start index of reverse transform.</param>
/// <param name="numPixels">The number of pixels to apply the transform.</param>
public static void TransformColorInverse(Vp8LMultipliers m, Span<uint> pixelData, int start, int numPixels)
public static void TransformColorInverse(Vp8LMultipliers m, Span<uint> pixelData)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
Vector128<int> multsrb = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue));
Vector128<int> multsb2 = MkCst16(Cst5b(m.RedToBlue), 0);
var maskalphagreen = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
var shufflemask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0);
fixed (uint* src = pixelData)
{
int idx;
for (idx = 0; idx + 4 <= pixelData.Length; idx += 4)
{
var pos = src + idx;
Vector128<uint> input = Sse2.LoadVector128(pos);
Vector128<byte> a = Sse2.And(input.AsByte(), maskalphagreen);
Vector128<short> b = Sse2.ShuffleLow(a.AsInt16(), shufflemask);
Vector128<short> c = Sse2.ShuffleHigh(b.AsInt16(), shufflemask);
Vector128<short> d = Sse2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16());
Vector128<byte> e = Sse2.Add(input.AsByte(), d.AsByte());
Vector128<short> f = Sse2.ShiftLeftLogical(e.AsInt16(), 8);
Vector128<short> g = Sse2.MultiplyHigh(f, multsb2.AsInt16());
Vector128<int> h = Sse2.ShiftRightLogical(g.AsInt32(), 8);
Vector128<byte> i = Sse2.Add(h.AsByte(), f.AsByte());
Vector128<short> j = Sse2.ShiftRightLogical(i.AsInt16(), 8);
Vector128<byte> output = Sse2.Or(j.AsByte(), a);
Sse2.Store((byte*)pos, output);
}
if (idx != pixelData.Length)
{
TransformColorInverseNoneVectorized(m, pixelData.Slice(idx));
}
}
}
else
#endif
{
TransformColorInverseNoneVectorized(m, pixelData);
}
}
private static void TransformColorInverseNoneVectorized(Vp8LMultipliers m, Span<uint> pixelData)
{
int end = start + numPixels;
for (int i = start; i < end; i++)
for (int i = 0; i < pixelData.Length; i++)
{
uint argb = pixelData[i];
sbyte green = (sbyte)(argb >> 8);

30
tests/ImageSharp.Benchmarks/Codecs/DecodeWebp.cs

@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
return image.Height;
}
/* Results 15.05.2020
/* Results 26.12.2020
* BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362
Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.202
@ -82,20 +82,20 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
Job-WMTYOZ : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
IterationCount=3 LaunchCount=1 WarmupCount=3
| Method | Runtime | TestImageLossy | TestImageLossless | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|--------------------------- |-------------- |--------------------- |--------------------- |-----------:|----------:|--------:|----------:|----------:|------:|-------------:|
| 'Magick Lossy WebP' | .NET 4.7.2 | WebP/(...).webp [21] | WebP/(...).webp [24] | 125.2 ms | 7.93 ms | 0.43 ms | - | - | - | 18.05 KB |
| 'ImageSharp Lossy Webp' | .NET 4.7.2 | WebP/(...).webp [21] | WebP/(...).webp [24] | 1,102.1 ms | 67.88 ms | 3.72 ms | 2000.0000 | - | - | 11835.55 KB |
| 'Magick Lossless WebP' | .NET 4.7.2 | WebP/(...).webp [21] | WebP/(...).webp [24] | 183.6 ms | 7.11 ms | 0.39 ms | - | - | - | 18.71 KB |
| 'ImageSharp Lossless Webp' | .NET 4.7.2 | WebP/(...).webp [21] | WebP/(...).webp [24] | 1,820.1 ms | 68.66 ms | 3.76 ms | 4000.0000 | 1000.0000 | - | 223765.64 KB |
| 'Magick Lossy WebP' | .NET Core 2.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 124.7 ms | 1.92 ms | 0.11 ms | - | - | - | 15.97 KB |
| 'ImageSharp Lossy Webp' | .NET Core 2.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 739.0 ms | 39.51 ms | 2.17 ms | 2000.0000 | - | - | 11802.98 KB |
| 'Magick Lossless WebP' | .NET Core 2.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 184.0 ms | 21.65 ms | 1.19 ms | - | - | - | 17.96 KB |
| 'ImageSharp Lossless Webp' | .NET Core 2.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 618.3 ms | 16.33 ms | 0.90 ms | 4000.0000 | 1000.0000 | - | 223699.11 KB |
| 'Magick Lossy WebP' | .NET Core 3.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 125.6 ms | 17.51 ms | 0.96 ms | - | - | - | 16.1 KB |
| 'ImageSharp Lossy Webp' | .NET Core 3.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 768.4 ms | 114.73 ms | 6.29 ms | 2000.0000 | - | - | 11802.89 KB |
| 'Magick Lossless WebP' | .NET Core 3.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 183.6 ms | 3.32 ms | 0.18 ms | - | - | - | 17 KB |
| 'ImageSharp Lossless Webp' | .NET Core 3.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 621.3 ms | 12.12 ms | 0.66 ms | 4000.0000 | 1000.0000 | - | 223698.75 KB |
| Method | Job | Runtime | TestImageLossy | TestImageLossless | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|--------------------------- |----------- |-------------- |--------------------- |--------------------- |-----------:|---------:|--------:|----------:|----------:|------:|------------:|
| 'Magick Lossy WebP' | Job-TNALDZ | .NET 4.7.2 | WebP/(...).webp [21] | WebP/(...).webp [24] | 107.1 ms | 47.56 ms | 2.61 ms | - | - | - | 32.05 KB |
| 'ImageSharp Lossy Webp' | Job-TNALDZ | .NET 4.7.2 | WebP/(...).webp [21] | WebP/(...).webp [24] | 1,108.4 ms | 25.90 ms | 1.42 ms | - | - | - | 2779.53 KB |
| 'Magick Lossless WebP' | Job-TNALDZ | .NET 4.7.2 | WebP/(...).webp [21] | WebP/(...).webp [24] | 145.8 ms | 8.97 ms | 0.49 ms | - | - | - | 18.05 KB |
| 'ImageSharp Lossless Webp' | Job-TNALDZ | .NET 4.7.2 | WebP/(...).webp [21] | WebP/(...).webp [24] | 1,662.9 ms | 9.34 ms | 0.51 ms | 4000.0000 | 1000.0000 | - | 30556.87 KB |
| 'Magick Lossy WebP' | Job-ATRTFL | .NET Core 2.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 106.2 ms | 14.80 ms | 0.81 ms | - | - | - | 16 KB |
| 'ImageSharp Lossy Webp' | Job-ATRTFL | .NET Core 2.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 743.1 ms | 7.53 ms | 0.41 ms | - | - | - | 2767.8 KB |
| 'Magick Lossless WebP' | Job-ATRTFL | .NET Core 2.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 146.7 ms | 25.23 ms | 1.38 ms | - | - | - | 16.76 KB |
| 'ImageSharp Lossless Webp' | Job-ATRTFL | .NET Core 2.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 529.2 ms | 64.09 ms | 3.51 ms | 4000.0000 | 1000.0000 | - | 22859.97 KB |
| 'Magick Lossy WebP' | Job-TMFWEM | .NET Core 3.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 106.0 ms | 9.51 ms | 0.52 ms | - | - | - | 15.71 KB |
| 'ImageSharp Lossy Webp' | Job-TMFWEM | .NET Core 3.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 765.8 ms | 34.82 ms | 1.91 ms | - | - | - | 2767.79 KB |
| 'Magick Lossless WebP' | Job-TMFWEM | .NET Core 3.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 146.0 ms | 25.51 ms | 1.40 ms | - | - | - | 16.02 KB |
| 'ImageSharp Lossless Webp' | Job-TMFWEM | .NET Core 3.1 | WebP/(...).webp [21] | WebP/(...).webp [24] | 478.3 ms | 89.70 ms | 4.92 ms | 4000.0000 | 1000.0000 | - | 22859.61 KB |
*/
}
}

52
tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs

@ -100,6 +100,38 @@ namespace SixLabors.ImageSharp.Tests.Formats.WebP
Assert.Equal(expectedOutput, pixelData);
}
private static void RunTransformColorInverseTest()
{
uint[] pixelData =
{
100279, 65790, 16710907, 16712190, 130813, 65028, 131840, 264449, 133377, 65790, 61697, 15917319,
14801924, 16317698, 591614, 394748, 16711935, 131072, 65792, 16711679, 328704, 656896, 132607,
328703, 197120, 66563, 16646657, 196607, 130815, 16711936, 131587, 131326, 66049, 261632, 16711936,
16776960, 3, 511, 65792, 16711938, 16580612, 65535, 65019, 327425, 16516097, 261377, 196861, 66049,
16711680, 65027, 16712962
};
var m = new Vp8LMultipliers()
{
GreenToBlue = 240,
GreenToRed = 232,
RedToBlue = 0
};
uint[] expectedOutput =
{
5998579, 65790, 130301, 16646653, 196350, 130565, 16712702, 16583164, 16452092, 65790, 782600,
647446, 16571414, 16448771, 263931, 132601, 16711935, 131072, 511, 16711679, 132350, 329469,
16647676, 132093, 66303, 16647169, 16515584, 196607, 196096, 16646655, 514, 131326, 16712192,
327169, 16646655, 16776960, 3, 16712190, 511, 16646401, 16580612, 65535, 196092, 327425, 16319743,
392450, 196861, 16712192, 16711680, 130564, 16451071
};
LosslessUtils.TransformColorInverse(m, pixelData);
Assert.Equal(expectedOutput, pixelData);
}
[Fact]
public void SubtractGreen_Works()
{
@ -113,11 +145,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.WebP
}
[Fact]
public void TrannsformColor_Works()
public void TransformColor_Works()
{
RunTransformColorTest();
}
[Fact]
public void TransformColorInverse_Works()
{
RunTransformColorInverseTest();
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void SubtractGreen_WithHardwareIntrinsics_Works()
@ -166,6 +204,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.WebP
{
FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorTest, HwIntrinsics.DisableSSE2);
}
[Fact]
public void TransformColorInverse_WithHardwareIntrinsics_Works()
{
FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.AllowAll);
}
[Fact]
public void TransformColorInverse_WithoutSSE2_Works()
{
FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.DisableSSE2);
}
#endif
}
}

2
tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.WebP
public class PredictorEncoderTests
{
[Fact]
public void ColorSpaceTransform_ProducesExpectedData()
public static void ColorSpaceTransform_ProducesExpectedData()
{
RunColorSpaceTransformTest();
}

Loading…
Cancel
Save