From 399a10cd4f3f36a48c26878ae87557a8501d6f21 Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Sat, 14 May 2022 17:06:42 +0300 Subject: [PATCH] Fixed color conversion --- .../JpegColorConverter.FromCmykAvx.cs | 37 +++++++++-------- .../JpegColorConverter.FromCmykScalar.cs | 32 +++++++-------- .../JpegColorConverter.FromCmykVector.cs | 41 ++++++++++--------- .../JpegColorConverter.FromGrayScaleAvx.cs | 29 ++++++++++++- .../JpegColorConverter.FromGrayScaleScalar.cs | 19 ++++++--- .../JpegColorConverter.FromGrayScaleVector.cs | 32 +++++++++++++-- .../JpegColorConverter.FromRgbAvx.cs | 6 ++- .../JpegColorConverter.FromRgbScalar.cs | 13 ++++-- .../JpegColorConverter.FromRgbVector.cs | 11 +++-- .../JpegColorConverter.FromYCbCrAvx.cs | 35 ++++++++-------- .../JpegColorConverter.FromYCbCrScalar.cs | 26 ++++++------ .../JpegColorConverter.FromYCbCrVector.cs | 36 ++++++++-------- .../JpegColorConverter.FromYccKAvx.cs | 5 ++- .../JpegColorConverter.FromYccKScalar.cs | 2 +- .../JpegColorConverter.FromYccKVector.cs | 5 ++- .../ColorConverters/JpegColorConverterBase.cs | 2 +- .../JpegColorConverterVector.cs | 34 +++++++++------ .../Encoder/JpegComponentPostProcessor.cs | 9 ++-- .../Encoder/SpectralConverter{TPixel}.cs | 28 +++++++++++-- 19 files changed, 260 insertions(+), 142 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykAvx.cs index 0b170140ab..989d155fcd 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykAvx.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. #if SUPPORTS_RUNTIME_INTRINSICS +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -47,31 +48,33 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } } - public override void ConvertFromRgbInplace(in ComponentValues values) + public override void ConvertFromRgbInplace(in ComponentValues values, Span rLane, Span gLane, Span bLane) { - ref Vector256 c0Base = + ref Vector256 destC = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector256 c1Base = + ref Vector256 destM = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector256 c2Base = + ref Vector256 destY = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - ref Vector256 c3Base = + ref Vector256 destK = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + ref Vector256 srcR = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector256 srcG = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector256 srcB = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + // Used for the color conversion var scale = Vector256.Create(this.MaximumValue); nint n = values.Component0.Length / Vector256.Count; for (nint i = 0; i < n; i++) { - ref Vector256 c0 = ref Unsafe.Add(ref c0Base, i); - ref Vector256 c1 = ref Unsafe.Add(ref c1Base, i); - ref Vector256 c2 = ref Unsafe.Add(ref c2Base, i); - ref Vector256 c3 = ref Unsafe.Add(ref c3Base, i); - - Vector256 ctmp = Avx.Subtract(scale, c0); - Vector256 mtmp = Avx.Subtract(scale, c1); - Vector256 ytmp = Avx.Subtract(scale, c2); + Vector256 ctmp = Avx.Subtract(scale, Unsafe.Add(ref srcR, i)); + Vector256 mtmp = Avx.Subtract(scale, Unsafe.Add(ref srcG, i)); + Vector256 ytmp = Avx.Subtract(scale, Unsafe.Add(ref srcB, i)); Vector256 ktmp = Avx.Min(ctmp, Avx.Min(mtmp, ytmp)); Vector256 kMask = Avx.CompareNotEqual(ktmp, scale); @@ -80,10 +83,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components mtmp = Avx.And(Avx.Divide(Avx.Subtract(mtmp, ktmp), Avx.Subtract(scale, ktmp)), kMask); ytmp = Avx.And(Avx.Divide(Avx.Subtract(ytmp, ktmp), Avx.Subtract(scale, ktmp)), kMask); - c0 = Avx.Subtract(scale, Avx.Multiply(ctmp, scale)); - c1 = Avx.Subtract(scale, Avx.Multiply(mtmp, scale)); - c2 = Avx.Subtract(scale, Avx.Multiply(ytmp, scale)); - c3 = Avx.Subtract(scale, ktmp); + Unsafe.Add(ref destC, i) = Avx.Subtract(scale, Avx.Multiply(ctmp, scale)); + Unsafe.Add(ref destM, i) = Avx.Subtract(scale, Avx.Multiply(mtmp, scale)); + Unsafe.Add(ref destY, i) = Avx.Subtract(scale, Avx.Multiply(ytmp, scale)); + Unsafe.Add(ref destK, i) = Avx.Subtract(scale, ktmp); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykScalar.cs index 13352ba7b5..af3decaad0 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykScalar.cs @@ -17,8 +17,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components public override void ConvertToRgbInplace(in ComponentValues values) => ConvertToRgbInplace(values, this.MaximumValue); - public override void ConvertFromRgbInplace(in ComponentValues values) - => ConvertFromRgbInplace(values, this.MaximumValue); + public override void ConvertFromRgbInplace(in ComponentValues values, Span r, Span g, Span b) + => ConvertFromRgbInplace(values, this.MaximumValue, r, g, b); public static void ConvertToRgbInplace(in ComponentValues values, float maxValue) { @@ -42,21 +42,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } } - public static void ConvertFromRgbInplace(in ComponentValues values, float maxValue) + public static void ConvertFromRgbInplace(in ComponentValues values, float maxValue, Span r, Span g, Span b) { - Span c0 = values.Component0; - Span c1 = values.Component1; - Span c2 = values.Component2; - Span c3 = values.Component3; + Span c = values.Component0; + Span m = values.Component1; + Span y = values.Component2; + Span k = values.Component3; - for (int i = 0; i < c0.Length; i++) + for (int i = 0; i < c.Length; i++) { - float ctmp = 255f - c0[i]; - float mtmp = 255f - c1[i]; - float ytmp = 255f - c2[i]; + float ctmp = 255f - r[i]; + float mtmp = 255f - g[i]; + float ytmp = 255f - b[i]; float ktmp = MathF.Min(MathF.Min(ctmp, mtmp), ytmp); - if (255f - ktmp <= float.Epsilon) + if (ktmp >= 255f) { ctmp = 0f; mtmp = 0f; @@ -69,10 +69,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components ytmp = (ytmp - ktmp) / (255f - ktmp); } - c0[i] = maxValue - (ctmp * maxValue); - c1[i] = maxValue - (mtmp * maxValue); - c2[i] = maxValue - (ytmp * maxValue); - c3[i] = maxValue - ktmp; + c[i] = maxValue - (ctmp * maxValue); + m[i] = maxValue - (mtmp * maxValue); + y[i] = maxValue - (ytmp * maxValue); + k[i] = maxValue - ktmp; } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykVector.cs index d445546050..6a6fd4f5f5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromCmykVector.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -47,31 +48,33 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components protected override void ConvertCoreInplaceToRgb(in ComponentValues values) => FromCmykScalar.ConvertToRgbInplace(values, this.MaximumValue); - protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values) + protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) { - ref Vector c0Base = + ref Vector destC = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector c1Base = + ref Vector destM = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector c2Base = + ref Vector destY = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - ref Vector c3Base = + ref Vector destK = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + ref Vector srcR = + ref Unsafe.As>(ref MemoryMarshal.GetReference(r)); + ref Vector srcG = + ref Unsafe.As>(ref MemoryMarshal.GetReference(g)); + ref Vector srcB = + ref Unsafe.As>(ref MemoryMarshal.GetReference(b)); + // Used for the color conversion var scale = new Vector(this.MaximumValue); nint n = values.Component0.Length / Vector.Count; for (nint i = 0; i < n; i++) { - ref Vector c0 = ref Unsafe.Add(ref c0Base, i); - ref Vector c1 = ref Unsafe.Add(ref c1Base, i); - ref Vector c2 = ref Unsafe.Add(ref c2Base, i); - ref Vector c3 = ref Unsafe.Add(ref c3Base, i); - - Vector ctmp = scale - c0; - Vector mtmp = scale - c1; - Vector ytmp = scale - c2; + Vector ctmp = scale - Unsafe.Add(ref srcR, i); + Vector mtmp = scale - Unsafe.Add(ref srcG, i); + Vector ytmp = scale - Unsafe.Add(ref srcB, i); Vector ktmp = Vector.Min(ctmp, Vector.Min(mtmp, ytmp)); var kMask = Vector.Equals(ktmp, scale); @@ -79,15 +82,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components mtmp = Vector.AndNot((mtmp - ktmp) / (scale - ktmp), kMask.As()); ytmp = Vector.AndNot((ytmp - ktmp) / (scale - ktmp), kMask.As()); - c0 = scale - (ctmp * scale); - c1 = scale - (mtmp * scale); - c2 = scale - (ytmp * scale); - c3 = scale - ktmp; + Unsafe.Add(ref destC, i) = scale - (ctmp * scale); + Unsafe.Add(ref destM, i) = scale - (mtmp * scale); + Unsafe.Add(ref destY, i) = scale - (ytmp * scale); + Unsafe.Add(ref destK, i) = scale - ktmp; } } - protected override void ConvertCoreInplaceFromRgb(in ComponentValues values) - => FromCmykScalar.ConvertFromRgbInplace(values, this.MaximumValue); + protected override void ConvertCoreInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) + => FromCmykScalar.ConvertFromRgbInplace(values, this.MaximumValue, r, g, b); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs index c38545b41c..faa50ba476 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs @@ -2,10 +2,12 @@ // Licensed under the Apache License, Version 2.0. #if SUPPORTS_RUNTIME_INTRINSICS +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; +using static SixLabors.ImageSharp.SimdUtils; namespace SixLabors.ImageSharp.Formats.Jpeg.Components { @@ -22,8 +24,33 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { } - public override void ConvertFromRgbInplace(in ComponentValues values) + public override void ConvertFromRgbInplace(in ComponentValues values, Span rLane, Span gLane, Span bLane) { + ref Vector256 destLuminance = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + + ref Vector256 srcRed = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector256 srcGreen = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector256 srcBlue = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + + // Used for the color conversion + var f0299 = Vector256.Create(0.299f); + var f0587 = Vector256.Create(0.587f); + var f0114 = Vector256.Create(0.114f); + + nint n = values.Component0.Length / Vector256.Count; + for (nint i = 0; i < n; i++) + { + ref Vector256 r = ref Unsafe.Add(ref srcRed, i); + ref Vector256 g = ref Unsafe.Add(ref srcGreen, i); + ref Vector256 b = ref Unsafe.Add(ref srcBlue, i); + + // luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b) + Unsafe.Add(ref destLuminance, i) = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); + } } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs index 5981bf1ac7..2430606229 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Jpeg.Components { @@ -19,15 +17,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components public override void ConvertToRgbInplace(in ComponentValues values) => ConvertCoreInplaceToRgb(values.Component0, this.MaximumValue); - public override void ConvertFromRgbInplace(in ComponentValues values) - => ConvertCoreInplaceFromRgb(values.Component0, this.MaximumValue); + public override void ConvertFromRgbInplace(in ComponentValues values, Span r, Span g, Span b) + => ConvertCoreInplaceFromRgb(values, r, g, b); internal static void ConvertCoreInplaceToRgb(Span values, float maxValue) { } - internal static void ConvertCoreInplaceFromRgb(Span values, float maxValue) + internal static void ConvertCoreInplaceFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) { + Span c0 = values.Component0; + + for (int i = 0; i < c0.Length; i++) + { + float r = rLane[i]; + float g = gLane[i]; + float b = bLane[i]; + + // luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b) + c0[i] = (0.299f * r) + (0.587f * g) + (0.114f * b); + } } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs index 3ffacb909a..f92b283d42 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -34,13 +35,36 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components protected override void ConvertCoreInplaceToRgb(in ComponentValues values) => FromGrayscaleScalar.ConvertCoreInplaceToRgb(values.Component0, this.MaximumValue); - protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values) + protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) { - } + ref Vector destLuma = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - protected override void ConvertCoreInplaceFromRgb(in ComponentValues values) - { + ref Vector srcR = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector srcG = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector srcB = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + + var rMult = new Vector(0.299f); + var gMult = new Vector(0.587f); + var bMult = new Vector(0.114f); + + nint n = values.Component0.Length / Vector.Count; + for (nint i = 0; i < n; i++) + { + Vector r = Unsafe.Add(ref srcR, i); + Vector g = Unsafe.Add(ref srcR, i); + Vector b = Unsafe.Add(ref srcR, i); + + // luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b) + Unsafe.Add(ref destLuma, i) = (rMult * r) + (gMult * g) + (bMult * b); + } } + + protected override void ConvertCoreInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) + => FromGrayscaleScalar.ConvertCoreInplaceFromRgb(values, r, g, b); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbAvx.cs index 070311e119..141f323ec8 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbAvx.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. #if SUPPORTS_RUNTIME_INTRINSICS +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -41,8 +42,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } } - public override void ConvertFromRgbInplace(in ComponentValues values) + public override void ConvertFromRgbInplace(in ComponentValues values, Span rLane, Span gLane, Span bLane) { + rLane.CopyTo(values.Component0); + gLane.CopyTo(values.Component1); + bLane.CopyTo(values.Component2); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbScalar.cs index db61359121..64b357d54e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbScalar.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; + namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal abstract partial class JpegColorConverterBase @@ -13,10 +15,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } public override void ConvertToRgbInplace(in ComponentValues values) - => ConvertCoreInplaceFromRgb(values, this.MaximumValue); + => ConvertCoreInplaceToRgb(values, this.MaximumValue); - public override void ConvertFromRgbInplace(in ComponentValues values) - => ConvertCoreInplaceFromRgb(values, this.MaximumValue); + public override void ConvertFromRgbInplace(in ComponentValues values, Span r, Span g, Span b) + => ConvertCoreInplaceFromRgb(values, r, g, b); internal static void ConvertCoreInplaceToRgb(ComponentValues values, float maxValue) { @@ -25,8 +27,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components FromGrayscaleScalar.ConvertCoreInplaceToRgb(values.Component2, maxValue); } - internal static void ConvertCoreInplaceFromRgb(ComponentValues values, float maxValue) + internal static void ConvertCoreInplaceFromRgb(ComponentValues values, Span r, Span g, Span b) { + r.CopyTo(values.Component0); + g.CopyTo(values.Component1); + b.CopyTo(values.Component2); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbVector.cs index f16bf8178d..72a8ef1298 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromRgbVector.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -42,13 +43,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components protected override void ConvertCoreInplaceToRgb(in ComponentValues values) => FromRgbScalar.ConvertCoreInplaceToRgb(values, this.MaximumValue); - protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values) + protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) { + r.CopyTo(values.Component0); + g.CopyTo(values.Component1); + b.CopyTo(values.Component2); } - protected override void ConvertCoreInplaceFromRgb(in ComponentValues values) - { - } + protected override void ConvertCoreInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) + => FromRgbScalar.ConvertCoreInplaceFromRgb(values, r, g, b); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs index 6df2b714bc..678df453c8 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. #if SUPPORTS_RUNTIME_INTRINSICS +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -69,15 +70,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } } - public override void ConvertFromRgbInplace(in ComponentValues values) + public override void ConvertFromRgbInplace(in ComponentValues values, Span rLane, Span gLane, Span bLane) { - ref Vector256 c0Base = + ref Vector256 destY = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector256 c1Base = + ref Vector256 destCb = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector256 c2Base = + ref Vector256 destCr = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector256 srcR = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector256 srcG = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector256 srcB = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + // Used for the color conversion var chromaOffset = Vector256.Create(this.HalfValue); @@ -93,16 +101,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components nint n = values.Component0.Length / Vector256.Count; for (nint i = 0; i < n; i++) { - ref Vector256 c0 = ref Unsafe.Add(ref c0Base, i); - ref Vector256 c1 = ref Unsafe.Add(ref c1Base, i); - ref Vector256 c2 = ref Unsafe.Add(ref c2Base, i); - - // Vector256 r = Avx.Multiply(c0, scale); - // Vector256 g = Avx.Multiply(c1, scale); - // Vector256 b = Avx.Multiply(c2, scale); - Vector256 r = c0; - Vector256 g = c1; - Vector256 b = c2; + Vector256 r = Unsafe.Add(ref srcR, i); + Vector256 g = Unsafe.Add(ref srcG, i); + Vector256 b = Unsafe.Add(ref srcB, i); // y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b) // cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b) @@ -111,9 +112,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components Vector256 cb = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r)); Vector256 cr = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r)); - c0 = y; - c1 = cb; - c2 = cr; + Unsafe.Add(ref destY, i) = y; + Unsafe.Add(ref destCb, i) = cb; + Unsafe.Add(ref destCr, i) = cr; } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs index 8e17c51300..424ab83eb3 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs @@ -23,8 +23,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components public override void ConvertToRgbInplace(in ComponentValues values) => ConvertCoreInplaceToRgb(values, this.MaximumValue, this.HalfValue); - public override void ConvertFromRgbInplace(in ComponentValues values) - => ConvertCoreInplaceFromRgb(values, this.MaximumValue, this.HalfValue); + public override void ConvertFromRgbInplace(in ComponentValues values, Span r, Span g, Span b) + => ConvertCoreInplaceFromRgb(values, this.HalfValue, r, g, b); public static void ConvertCoreInplaceToRgb(in ComponentValues values, float maxValue, float halfValue) { @@ -49,24 +49,24 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } } - public static void ConvertCoreInplaceFromRgb(in ComponentValues values, float maxValue, float halfValue) + public static void ConvertCoreInplaceFromRgb(in ComponentValues values, float halfValue, Span rLane, Span gLane, Span bLane) { - Span c0 = values.Component0; - Span c1 = values.Component1; - Span c2 = values.Component2; + Span y = values.Component0; + Span cb = values.Component1; + Span cr = values.Component2; - for (int i = 0; i < c0.Length; i++) + for (int i = 0; i < y.Length; i++) { - float r = c0[i]; - float g = c1[i]; - float b = c2[i]; + float r = rLane[i]; + float g = gLane[i]; + float b = bLane[i]; // y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b) // cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b) // cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b) - c0[i] = (0.299f * r) + (0.587f * g) + (0.114f * b); - c1[i] = halfValue - (0.168736f * r) - (0.331264f * g) + (0.5f * b); - c2[i] = halfValue + (0.5f * r) - (0.418688f * g) - (0.081312f * b); + y[i] = (0.299f * r) + (0.587f * g) + (0.114f * b); + cb[i] = halfValue - (0.168736f * r) - (0.331264f * g) + (0.5f * b); + cr[i] = halfValue + (0.5f * r) - (0.418688f * g) - (0.081312f * b); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrVector.cs index 34790e41b6..022c09b497 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYCbCrVector.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -70,15 +71,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components protected override void ConvertCoreInplaceToRgb(in ComponentValues values) => FromYCbCrScalar.ConvertCoreInplaceToRgb(values, this.MaximumValue, this.HalfValue); - protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values) + protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) { - ref Vector c0Base = + ref Vector destY = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector c1Base = + ref Vector destCb = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector c2Base = + ref Vector destCr = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector srcR = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector srcG = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector srcB = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + var chromaOffset = new Vector(this.HalfValue); var rYMult = new Vector(0.299f); @@ -96,25 +104,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components nint n = values.Component0.Length / Vector.Count; for (nint i = 0; i < n; i++) { - ref Vector c0 = ref Unsafe.Add(ref c0Base, i); - ref Vector c1 = ref Unsafe.Add(ref c1Base, i); - ref Vector c2 = ref Unsafe.Add(ref c2Base, i); - - Vector r = c0; - Vector g = c1; - Vector b = c2; + Vector r = Unsafe.Add(ref srcR, i); + Vector g = Unsafe.Add(ref srcG, i); + Vector b = Unsafe.Add(ref srcB, i); // y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b) // cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b) // cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b) - c0 = (rYMult * r) + (gYMult * g) + (bYMult * b); - c1 = chromaOffset - (rCbMult * r) - (gCbMult * g) + (bCbMult * b); - c2 = chromaOffset + (rCrMult * r) - (gCrMult * g) - (bCrMult * b); + Unsafe.Add(ref destY, i) = (rYMult * r) + (gYMult * g) + (bYMult * b); + Unsafe.Add(ref destCb, i) = chromaOffset - (rCbMult * r) - (gCbMult * g) + (bCbMult * b); + Unsafe.Add(ref destCr, i) = chromaOffset + (rCrMult * r) - (gCrMult * g) - (bCrMult * b); } } - protected override void ConvertCoreInplaceFromRgb(in ComponentValues values) - => FromYCbCrScalar.ConvertCoreInplaceFromRgb(values, this.MaximumValue, this.HalfValue); + protected override void ConvertCoreInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) + => FromYCbCrScalar.ConvertCoreInplaceFromRgb(values, this.HalfValue, r, g, b); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKAvx.cs index d75d8277c8..6166f342b3 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKAvx.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. #if SUPPORTS_RUNTIME_INTRINSICS +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -77,8 +78,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } } - public override void ConvertFromRgbInplace(in ComponentValues values) - => throw new System.NotImplementedException(); + public override void ConvertFromRgbInplace(in ComponentValues values, Span r, Span g, Span b) + => throw new NotImplementedException(); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKScalar.cs index 353d085e8e..bf5d1c4140 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKScalar.cs @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } } - public override void ConvertFromRgbInplace(in ComponentValues values) + public override void ConvertFromRgbInplace(in ComponentValues values, Span r, Span g, Span b) => throw new NotImplementedException(); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKVector.cs index c8967f1f10..cc4212d473 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.FromYccKVector.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -71,10 +72,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components protected override void ConvertCoreInplaceToRgb(in ComponentValues values) => FromYccKScalar.ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue); - protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values) + protected override void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) => throw new System.NotImplementedException(); - protected override void ConvertCoreInplaceFromRgb(in ComponentValues values) + protected override void ConvertCoreInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b) => throw new System.NotImplementedException(); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs index df1f8db8fc..500c25a951 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// The input/ouptut as a stack-only struct public abstract void ConvertToRgbInplace(in ComponentValues values); - public abstract void ConvertFromRgbInplace(in ComponentValues values); + public abstract void ConvertFromRgbInplace(in ComponentValues values, Span r, Span g, Span b); /// /// Returns the s for all supported colorspaces and precisions. diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs index 6b7621d8fb..b5460d31aa 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs @@ -35,13 +35,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components int length = values.Component0.Length; int remainder = (int)((uint)length % (uint)Vector.Count); - // Jpeg images are guaranteed to have pixel strides at least 8 pixels wide - // Thus there's no need to check whether simdCount is greater than zero int simdCount = length - remainder; - this.ConvertCoreVectorizedInplaceToRgb(values.Slice(0, simdCount)); + if (simdCount > 0) + { + this.ConvertCoreVectorizedInplaceToRgb(values.Slice(0, simdCount)); + } // Jpeg images width is always divisible by 8 without a remainder - // so it's safe to say SSE/AVX implementations would never have + // so it's safe to say SSE/AVX1/AVX2 implementations would never have // 'remainder' pixels // But some exotic simd implementations e.g. AVX-512 can have // remainder pixels @@ -51,26 +52,35 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } } - public override void ConvertFromRgbInplace(in ComponentValues values) + public override void ConvertFromRgbInplace(in ComponentValues values, Span r, Span g, Span b) { DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware."); int length = values.Component0.Length; int remainder = (int)((uint)length % (uint)Vector.Count); - // Jpeg images are guaranteed to have pixel strides at least 8 pixels wide - // Thus there's no need to check whether simdCount is greater than zero int simdCount = length - remainder; - this.ConvertCoreVectorizedInplaceFromRgb(values.Slice(0, simdCount)); + if (simdCount > 0) + { + this.ConvertCoreVectorizedInplaceFromRgb( + values.Slice(0, simdCount), + r.Slice(0, simdCount), + g.Slice(0, simdCount), + b.Slice(0, simdCount)); + } // Jpeg images width is always divisible by 8 without a remainder - // so it's safe to say SSE/AVX implementations would never have + // so it's safe to say SSE/AVX1/AVX2 implementations would never have // 'remainder' pixels // But some exotic simd implementations e.g. AVX-512 can have // remainder pixels if (remainder > 0) { - this.ConvertCoreInplaceFromRgb(values.Slice(simdCount, remainder)); + this.ConvertCoreInplaceFromRgb( + values.Slice(simdCount, remainder), + r.Slice(simdCount, remainder), + g.Slice(simdCount, remainder), + b.Slice(simdCount, remainder)); } } @@ -78,9 +88,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components protected abstract void ConvertCoreInplaceToRgb(in ComponentValues values); - protected abstract void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values); + protected abstract void ConvertCoreVectorizedInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b); - protected abstract void ConvertCoreInplaceFromRgb(in ComponentValues values); + protected abstract void ConvertCoreInplaceFromRgb(in ComponentValues values, Span r, Span g, Span b); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponentPostProcessor.cs index aa1c4b2c59..fb9f72a1a1 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/JpegComponentPostProcessor.cs @@ -60,14 +60,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder this.PackColorBuffer(); } - for (int y = 0; y < spectralBuffer.Height; y++) + int blocksRowsPerStep = this.component.SamplingFactors.Height; + + for (int y = 0; y < blocksRowsPerStep; y++) { int yBuffer = y * this.blockAreaSize.Height; + Span colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer); + Span blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y); for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++) { - Span colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer); - Span blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y); - // load 8x8 block from 8 pixel strides int xColorBufferStart = xBlock * 8; workspaceBlock.ScaledCopyFrom( diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs index f645cb460e..3445072858 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs @@ -23,6 +23,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder private Buffer2D pixelBuffer; + private IMemoryOwner redLane; + + private IMemoryOwner greenLane; + + private IMemoryOwner blueLane; + private int alignedPixelWidth; private JpegColorConverterBase colorConverter; @@ -55,6 +61,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder this.componentProcessors[i] = new JpegComponentPostProcessor(allocator, component, postProcessorBufferSize, dequantTables[component.QuantizationTableIndex]); } + this.redLane = allocator.Allocate(this.alignedPixelWidth); + this.greenLane = allocator.Allocate(this.alignedPixelWidth); + this.blueLane = allocator.Allocate(this.alignedPixelWidth); + // color converter from Rgb24 to YCbCr this.colorConverter = JpegColorConverterBase.GetConverter(colorSpace: frame.ColorSpace, precision: 8); } @@ -67,6 +77,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder this.ConvertStride(spectralStep: 0); } + public void ConvertFull() + { + int steps = (int)Numerics.DivideCeil((uint)this.pixelBuffer.Height, (uint)this.pixelRowsPerStep); + for (int i = 0; i < steps; i++) + { + this.ConvertStride(i); + } + } + private void ConvertStride(int spectralStep) { // 1. Unpack from TPixel to r/g/b planes @@ -75,6 +94,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder // 4. Convert color buffer to spectral blocks with component post processors int maxY = Math.Min(this.pixelBuffer.Height, this.pixelRowCounter + this.pixelRowsPerStep); + Span rLane = this.redLane.GetSpan(); + Span gLane = this.greenLane.GetSpan(); + Span bLane = this.blueLane.GetSpan(); for (int yy = this.pixelRowCounter; yy < maxY; yy++) { int y = yy - this.pixelRowCounter; @@ -82,10 +104,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder // unpack TPixel to r/g/b planes Span sourceRow = this.pixelBuffer.DangerousGetRowSpan(yy); - var values = new JpegColorConverterBase.ComponentValues(this.componentProcessors, y); - PixelOperations.Instance.UnpackIntoRgbPlanes(this.configuration, values.Component0, values.Component1, values.Component2, sourceRow); + PixelOperations.Instance.UnpackIntoRgbPlanes(this.configuration, rLane, gLane, bLane, sourceRow); - this.colorConverter.ConvertFromRgbInplace(values); + var values = new JpegColorConverterBase.ComponentValues(this.componentProcessors, y); + this.colorConverter.ConvertFromRgbInplace(values, rLane, gLane, bLane); } for (int i = 0; i < this.componentProcessors.Length; i++)