From ef767cc9051530b579ea0e40f582b4bb452ff966 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 2 Oct 2021 21:53:56 +0200 Subject: [PATCH] Cmyk & Grayscale inplace conversion --- .../JpegColorConverter.FromCmykAvx2.cs | 34 ++++++++++++++++++ .../JpegColorConverter.FromCmykBasic.cs | 24 +++++++++++++ .../JpegColorConverter.FromCmykVector8.cs | 31 ++++++++++++++++ .../JpegColorConverter.FromGrayScaleAvx2.cs | 21 +++++++++++ .../JpegColorConverter.FromGrayScaleBasic.cs | 33 +++++++++++++++++ .../JpegColorConverter.FromRgbBasic.cs | 36 ++----------------- .../ColorConverters/JpegColorConverter.cs | 25 ++++--------- .../Formats/Jpg/JpegColorConverterTests.cs | 7 ++-- 8 files changed, 157 insertions(+), 54 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx2.cs index f9334de73..634e0faaf 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx2.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx2.cs @@ -74,8 +74,42 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters #endif } + protected override void ConvertCoreVectorizedInplace(in ComponentValues values) + { + #if SUPPORTS_RUNTIME_INTRINSICS + ref Vector256 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector256 c1Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector256 c2Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector256 c3Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + + // Used for the color conversion + var scale = Vector256.Create(1 / this.MaximumValue); + + int n = values.Component0.Length / 8; + for (int i = 0; i < n; i++) + { + ref Vector256 c = ref Unsafe.Add(ref c0Base, i); + ref Vector256 m = ref Unsafe.Add(ref c1Base, i); + ref Vector256 y = ref Unsafe.Add(ref c2Base, i); + Vector256 k = Unsafe.Add(ref c3Base, i); + + k = Avx.Multiply(k, scale); + c = Avx.Multiply(Avx.Multiply(c, k), scale); + m = Avx.Multiply(Avx.Multiply(m, k), scale); + y = Avx.Multiply(Avx.Multiply(y, k), scale); + } +#endif + } + protected override void ConvertCore(in ComponentValues values, Span result) => FromCmykBasic.ConvertCore(values, result, this.MaximumValue); + + protected override void ConvertCoreInplace(in ComponentValues values) => + FromCmykBasic.ConvertCoreInplace(values, this.MaximumValue); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykBasic.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykBasic.cs index 6cbd52ec3..f4a9529e1 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykBasic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykBasic.cs @@ -20,6 +20,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters ConvertCore(values, result, this.MaximumValue); } + public override void ConvertToRgbInplace(in ComponentValues values) => + ConvertCoreInplace(values, this.MaximumValue); + internal static void ConvertCore(in ComponentValues values, Span result, float maxValue) { ReadOnlySpan cVals = values.Component0; @@ -49,6 +52,27 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters result[i] = v; } } + + internal static void ConvertCoreInplace(in ComponentValues values, float maxValue) + { + Span c0 = values.Component0; + Span c1 = values.Component1; + Span c2 = values.Component2; + Span c3 = values.Component3; + + float scale = 1 / maxValue; + for (int i = 0; i < c0.Length; i++) + { + float c = c0[i]; + float m = c1[i]; + float y = c2[i]; + float k = c3[i] / maxValue; + + c0[i] = c * k * scale; + c1[i] = m * k * scale; + c2[i] = y * k * scale; + } + } } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector8.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector8.cs index e75634b0f..b55ec2e0f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector8.cs @@ -64,8 +64,39 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters } } + protected override void ConvertCoreVectorizedInplace(in ComponentValues values) + { + ref Vector cBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector mBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector yBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector kBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + + var scale = new Vector(1 / this.MaximumValue); + + // Walking 8 elements at one step: + int n = values.Component0.Length / 8; + for (int i = 0; i < n; i++) + { + ref Vector c = ref Unsafe.Add(ref cBase, i); + ref Vector m = ref Unsafe.Add(ref mBase, i); + ref Vector y = ref Unsafe.Add(ref yBase, i); + Vector k = Unsafe.Add(ref kBase, i) * scale; + + c = (c * k) * scale; + m = (m * k) * scale; + y = (y * k) * scale; + } + } + protected override void ConvertCore(in ComponentValues values, Span result) => FromCmykBasic.ConvertCore(values, result, this.MaximumValue); + + protected override void ConvertCoreInplace(in ComponentValues values) => + FromCmykBasic.ConvertCoreInplace(values, this.MaximumValue); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx2.cs index 45846a6b5..4efabf64a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx2.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx2.cs @@ -56,8 +56,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters #endif } + protected override void ConvertCoreVectorizedInplace(in ComponentValues values) + { +#if SUPPORTS_RUNTIME_INTRINSICS + ref Vector256 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + + // Used for the color conversion + var scale = Vector256.Create(1 / this.MaximumValue); + + int n = values.Component0.Length / 8; + for (int i = 0; i < n; i++) + { + ref Vector256 c0 = ref Unsafe.Add(ref c0Base, i); + c0 = Avx.Multiply(c0, scale); + } +#endif + } + protected override void ConvertCore(in ComponentValues values, Span result) => FromGrayscaleBasic.ConvertCore(values, result, this.MaximumValue); + + protected override void ConvertCoreInplace(in ComponentValues values) => + FromGrayscaleBasic.ScaleValues(values.Component0, this.MaximumValue); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleBasic.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleBasic.cs index 0b7a220d9..faf6f203f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleBasic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleBasic.cs @@ -22,6 +22,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters ConvertCore(values, result, this.MaximumValue); } + public override void ConvertToRgbInplace(in ComponentValues values) => + ScaleValues(values.Component0, this.MaximumValue); + internal static void ConvertCore(in ComponentValues values, Span result, float maxValue) { var maximum = 1 / maxValue; @@ -38,6 +41,36 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters Unsafe.Add(ref dBase, i) = v; } } + + internal static void ScaleValues(Span values, float maxValue) + { + // TODO: Optimize this + Span vecValues = MemoryMarshal.Cast(values); + + var scaleVector = new Vector4(1 / maxValue); + + for (int i = 0; i < vecValues.Length; i++) + { + vecValues[i] *= scaleVector; + } + + values = values.Slice(vecValues.Length * 4); + if (values.Length > 0) + { + float scaleValue = 1f / maxValue; + values[0] *= scaleValue; + + if (values.Length > 1) + { + values[1] *= scaleValue; + + if (values.Length > 2) + { + values[2] *= scaleValue; + } + } + } + } } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbBasic.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbBasic.cs index 497c943a3..313583f4a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbBasic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbBasic.cs @@ -28,39 +28,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters internal static void ConvertCoreInplace(ComponentValues values, float maxValue) { - // TODO: Optimize this - ConvertComponent(values.Component0, maxValue); - ConvertComponent(values.Component1, maxValue); - ConvertComponent(values.Component2, maxValue); - - static void ConvertComponent(Span values, float maxValue) - { - Span vecValues = MemoryMarshal.Cast(values); - - var scaleVector = new Vector4(1 / maxValue); - - for (int i = 0; i < vecValues.Length; i++) - { - vecValues[i] *= scaleVector; - } - - values = values.Slice(vecValues.Length * 4); - if (values.Length > 0) - { - float scaleValue = 1f / maxValue; - values[0] *= scaleValue; - - if (values.Length > 1) - { - values[1] *= scaleValue; - - if (values.Length > 2) - { - values[2] *= scaleValue; - } - } - } - } + FromGrayscaleBasic.ScaleValues(values.Component0, maxValue); + FromGrayscaleBasic.ScaleValues(values.Component1, maxValue); + FromGrayscaleBasic.ScaleValues(values.Component2, maxValue); } internal static void ConvertCore(in ComponentValues values, Span result, float maxValue) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs index 4c07783d7..8efbf92b6 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs @@ -186,12 +186,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters public readonly Span Component0; /// - /// The component 1 (eg. Cb) + /// The component 1 (eg. Cb). In case of grayscale, it points to . /// public readonly Span Component1; /// - /// The component 2 (eg. Cr) + /// The component 2 (eg. Cr). In case of grayscale, it points to . /// public readonly Span Component2; @@ -210,22 +210,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters this.ComponentCount = componentBuffers.Count; this.Component0 = componentBuffers[0].GetRowSpan(row); - this.Component1 = Span.Empty; - this.Component2 = Span.Empty; - this.Component3 = Span.Empty; - - if (this.ComponentCount > 1) - { - this.Component1 = componentBuffers[1].GetRowSpan(row); - if (this.ComponentCount > 2) - { - this.Component2 = componentBuffers[2].GetRowSpan(row); - if (this.ComponentCount > 3) - { - this.Component3 = componentBuffers[3].GetRowSpan(row); - } - } - } + + // In case of grayscale, Component1 and Component2 point to Component0 memory area + this.Component1 = this.ComponentCount > 1 ? componentBuffers[1].GetRowSpan(row) : this.Component0; + this.Component2 = this.ComponentCount > 2 ? componentBuffers[2].GetRowSpan(row) : this.Component0; + this.Component3 = this.ComponentCount > 3 ? componentBuffers[3].GetRowSpan(row) : Span.Empty; } internal ComponentValues( diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index e275bf50c..6354d1e19 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -375,6 +375,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg float maxVal = 255f) { var rnd = new Random(seed); + var buffers = new Buffer2D[componentCount]; for (int i = 0; i < componentCount; i++) { @@ -429,9 +430,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg static JpegColorConverter.ComponentValues Copy(JpegColorConverter.ComponentValues values) { Span c0 = values.Component0.ToArray(); - Span c1 = values.ComponentCount > 1 ? values.Component1.ToArray().AsSpan() : default; - Span c2 = values.ComponentCount > 2 ? values.Component2.ToArray().AsSpan() : default; - Span c3 = values.ComponentCount > 3 ? values.Component3.ToArray().AsSpan() : default; + Span c1 = values.ComponentCount > 1 ? values.Component1.ToArray().AsSpan() : c0; + Span c2 = values.ComponentCount > 2 ? values.Component2.ToArray().AsSpan() : c0; + Span c3 = values.ComponentCount > 3 ? values.Component3.ToArray().AsSpan() : Span.Empty; return new JpegColorConverter.ComponentValues(values.ComponentCount, c0, c1, c2, c3); } }