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();
}