From cf7a6986f4933d8dfe43132f2381baf7bad13881 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 26 Dec 2020 09:52:03 +0100 Subject: [PATCH] Add SSE2 version of TransformColorInverse --- .../Formats/WebP/Lossless/LosslessUtils.cs | 62 ++++++++++++++++--- .../Codecs/DecodeWebp.cs | 30 ++++----- .../Formats/WebP/LosslessUtilsTests.cs | 52 +++++++++++++++- .../Formats/WebP/PredictorEncoderTests.cs | 2 +- 4 files changed, 121 insertions(+), 25 deletions(-) diff --git a/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs b/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs index 5b4f3bf72e..086ad54b01 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs +++ b/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 } } + /// + /// 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. + /// + /// The Vp8LMultipliers. + /// The pixel data to transform. + /// The number of pixels to process. public static void TransformColor(Vp8LMultipliers m, Span 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 data, int numPixels) + private static void TransformColorNoneVectorized(Vp8LMultipliers m, Span data, int numPixels) { for (int i = 0; i < numPixels; i++) { @@ -455,12 +461,52 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless /// /// The color transform element. /// The pixel data to apply the inverse transform on. - /// The start index of reverse transform. - /// The number of pixels to apply the transform. - public static void TransformColorInverse(Vp8LMultipliers m, Span pixelData, int start, int numPixels) + public static void TransformColorInverse(Vp8LMultipliers m, Span pixelData) + { +#if SUPPORTS_RUNTIME_INTRINSICS + if (Sse2.IsSupported) + { + Vector128 multsrb = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); + Vector128 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 input = Sse2.LoadVector128(pos); + Vector128 a = Sse2.And(input.AsByte(), maskalphagreen); + Vector128 b = Sse2.ShuffleLow(a.AsInt16(), shufflemask); + Vector128 c = Sse2.ShuffleHigh(b.AsInt16(), shufflemask); + Vector128 d = Sse2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16()); + Vector128 e = Sse2.Add(input.AsByte(), d.AsByte()); + Vector128 f = Sse2.ShiftLeftLogical(e.AsInt16(), 8); + Vector128 g = Sse2.MultiplyHigh(f, multsb2.AsInt16()); + Vector128 h = Sse2.ShiftRightLogical(g.AsInt32(), 8); + Vector128 i = Sse2.Add(h.AsByte(), f.AsByte()); + Vector128 j = Sse2.ShiftRightLogical(i.AsInt16(), 8); + Vector128 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 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); diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeWebp.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeWebp.cs index 28d69f4697..1ace21778c 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeWebp.cs +++ b/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 | */ } } diff --git a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs index afa6be0da2..8e0a14ccef 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs +++ b/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 } } diff --git a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs index cfd7bc184a..b989259a3c 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs +++ b/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(); }