From aebe375e2ccfd01949e6528d0986bce4175fad55 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 May 2025 19:50:29 +1000 Subject: [PATCH 01/11] Reduce JPEG color conversion to two pipelines --- src/ImageSharp/Common/Helpers/SimdUtils.cs | 38 +-- .../JpegColorConverter.CmykArm64.cs | 95 -------- .../JpegColorConverter.CmykAvx.cs | 95 -------- .../JpegColorConverter.CmykScalar.cs | 2 +- .../JpegColorConverter.GrayScaleArm.cs | 68 ------ .../JpegColorConverter.GrayScaleAvx.cs | 68 ------ .../JpegColorConverter.GrayScaleScalar.cs | 2 +- .../JpegColorConverter.RgbArm.cs | 53 ----- .../JpegColorConverter.RgbAvx.cs | 52 ----- .../JpegColorConverter.RgbScalar.cs | 2 +- .../JpegColorConverter.YCbCrArm.cs | 122 ---------- .../JpegColorConverter.YCbCrAvx.cs | 121 ---------- .../JpegColorConverter.YCbCrScalar.cs | 2 +- .../JpegColorConverter.YCbCrVector.cs | 36 +-- .../JpegColorConverter.YccKArm64.cs | 133 ----------- .../JpegColorConverter.YccKAvx.cs | 132 ----------- .../JpegColorConverter.YccKScalar.cs | 2 +- .../ColorConverters/JpegColorConverterArm.cs | 35 --- .../JpegColorConverterArm64.cs | 35 --- .../ColorConverters/JpegColorConverterAvx.cs | 34 --- .../ColorConverters/JpegColorConverterBase.cs | 123 +++------- .../JpegColorConverterVector.cs | 2 +- .../Decoder/SpectralConverter{TPixel}.cs | 2 +- .../ColorConversion/CmykColorConversion.cs | 20 +- .../GrayscaleColorConversion.cs | 14 +- .../ColorConversion/RgbColorConversion.cs | 20 +- .../ColorConversion/YCbCrColorConversion.cs | 20 +- .../ColorConversion/YccKColorConverter.cs | 20 +- .../Formats/Jpg/JpegColorConverterTests.cs | 218 ++---------------- 29 files changed, 99 insertions(+), 1467 deletions(-) delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykArm64.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrArm.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKArm64.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm64.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterAvx.cs diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs index 0279e57cc..273947272 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -1,10 +1,9 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; @@ -36,30 +35,37 @@ internal static partial class SimdUtils /// /// Rounds all values in 'v' to the nearest integer following semantics. - /// Source: - /// - /// https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L110 - /// /// /// The vector [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector FastRound(this Vector v) { - if (Avx2.IsSupported) + if (Avx512F.IsSupported && Vector.Count == Vector512.Count) + { + ref Vector512 v512 = ref Unsafe.As, Vector512>(ref v); + + // imm8 = 0b1000: + // imm8[7:4] = 0b0000 -> preserve 0 fractional bits (round to whole numbers) + // imm8[3:0] = 0b1000 -> _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC (round to nearest even, suppress exceptions) + Vector512 vRound = Avx512F.RoundScale(v512, 0b0000_1000); + return Unsafe.As, Vector>(ref vRound); + } + + if (Avx2.IsSupported && Vector.Count == Vector256.Count) { ref Vector256 v256 = ref Unsafe.As, Vector256>(ref v); Vector256 vRound = Avx.RoundToNearestInteger(v256); return Unsafe.As, Vector>(ref vRound); } - else - { - var magic0 = new Vector(int.MinValue); // 0x80000000 - var sgn0 = Vector.AsVectorSingle(magic0); - var and0 = Vector.BitwiseAnd(sgn0, v); - var or0 = Vector.BitwiseOr(and0, new Vector(8388608.0f)); - var add0 = Vector.Add(v, or0); - return Vector.Subtract(add0, or0); - } + + // https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L11 + Vector magic0 = new(int.MinValue); // 0x80000000 + Vector sgn0 = Vector.AsVectorSingle(magic0); + Vector and0 = Vector.BitwiseAnd(sgn0, v); + Vector or0 = Vector.BitwiseOr(and0, new Vector(8388608.0f)); + Vector add0 = Vector.Add(v, or0); + + return Vector.Subtract(add0, or0); } [Conditional("DEBUG")] diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykArm64.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykArm64.cs deleted file mode 100644 index 11122d3b8..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykArm64.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.Arm; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class CmykArm64 : JpegColorConverterArm64 - { - public CmykArm64(int precision) - : base(JpegColorSpace.Cmyk, precision) - { - } - - /// - public override void ConvertToRgbInplace(in ComponentValues values) - { - ref Vector128 c0Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector128 c1Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector128 c2Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - ref Vector128 c3Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); - - // Used for the color conversion - var scale = Vector128.Create(1 / (this.MaximumValue * this.MaximumValue)); - - nint n = (nint)(uint)values.Component0.Length / Vector128.Count; - for (nint i = 0; i < n; i++) - { - ref Vector128 c = ref Unsafe.Add(ref c0Base, i); - ref Vector128 m = ref Unsafe.Add(ref c1Base, i); - ref Vector128 y = ref Unsafe.Add(ref c2Base, i); - Vector128 k = Unsafe.Add(ref c3Base, i); - - k = AdvSimd.Multiply(k, scale); - c = AdvSimd.Multiply(c, k); - m = AdvSimd.Multiply(m, k); - y = AdvSimd.Multiply(y, k); - } - } - - /// - public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) - => ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); - - public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span rLane, Span gLane, Span bLane) - { - ref Vector128 destC = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector128 destM = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector128 destY = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - ref Vector128 destK = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); - - ref Vector128 srcR = - ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); - ref Vector128 srcG = - ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); - ref Vector128 srcB = - ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); - - var scale = Vector128.Create(maxValue); - - nint n = (nint)(uint)values.Component0.Length / Vector128.Count; - for (nint i = 0; i < n; i++) - { - Vector128 ctmp = AdvSimd.Subtract(scale, Unsafe.Add(ref srcR, i)); - Vector128 mtmp = AdvSimd.Subtract(scale, Unsafe.Add(ref srcG, i)); - Vector128 ytmp = AdvSimd.Subtract(scale, Unsafe.Add(ref srcB, i)); - Vector128 ktmp = AdvSimd.Min(ctmp, AdvSimd.Min(mtmp, ytmp)); - - Vector128 kMask = AdvSimd.Not(AdvSimd.CompareEqual(ktmp, scale)); - - ctmp = AdvSimd.And(AdvSimd.Arm64.Divide(AdvSimd.Subtract(ctmp, ktmp), AdvSimd.Subtract(scale, ktmp)), kMask); - mtmp = AdvSimd.And(AdvSimd.Arm64.Divide(AdvSimd.Subtract(mtmp, ktmp), AdvSimd.Subtract(scale, ktmp)), kMask); - ytmp = AdvSimd.And(AdvSimd.Arm64.Divide(AdvSimd.Subtract(ytmp, ktmp), AdvSimd.Subtract(scale, ktmp)), kMask); - - Unsafe.Add(ref destC, i) = AdvSimd.Subtract(scale, AdvSimd.Multiply(ctmp, scale)); - Unsafe.Add(ref destM, i) = AdvSimd.Subtract(scale, AdvSimd.Multiply(mtmp, scale)); - Unsafe.Add(ref destY, i) = AdvSimd.Subtract(scale, AdvSimd.Multiply(ytmp, scale)); - Unsafe.Add(ref destK, i) = AdvSimd.Subtract(scale, ktmp); - } - } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs deleted file mode 100644 index 76d188f45..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class CmykAvx : JpegColorConverterAvx - { - public CmykAvx(int precision) - : base(JpegColorSpace.Cmyk, precision) - { - } - - /// - public override void ConvertToRgbInplace(in ComponentValues values) - { - 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 * this.MaximumValue)); - - nuint n = values.Component0.Vector256Count(); - for (nuint 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(c, k); - m = Avx.Multiply(m, k); - y = Avx.Multiply(y, k); - } - } - - /// - public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) - => ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); - - public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span rLane, Span gLane, Span bLane) - { - ref Vector256 destC = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector256 destM = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector256 destY = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - 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)); - - var scale = Vector256.Create(maxValue); - - nuint n = values.Component0.Vector256Count(); - for (nuint i = 0; i < n; i++) - { - 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); - - ctmp = Avx.And(Avx.Divide(Avx.Subtract(ctmp, ktmp), Avx.Subtract(scale, ktmp)), kMask); - 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); - - 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.CmykScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs index 6d0013b88..1b3be69fa 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs @@ -13,7 +13,7 @@ internal abstract partial class JpegColorConverterBase } /// - public override void ConvertToRgbInplace(in ComponentValues values) => + public override void ConvertToRgbInPlace(in ComponentValues values) => ConvertToRgbInplace(values, this.MaximumValue); /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs deleted file mode 100644 index 6bb8fd7aa..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.Arm; -using static SixLabors.ImageSharp.SimdUtils; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class GrayscaleArm : JpegColorConverterArm - { - public GrayscaleArm(int precision) - : base(JpegColorSpace.Grayscale, precision) - { - } - - /// - public override void ConvertToRgbInplace(in ComponentValues values) - { - ref Vector128 c0Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - - // Used for the color conversion - var scale = Vector128.Create(1 / this.MaximumValue); - - nuint n = values.Component0.Vector128Count(); - for (nuint i = 0; i < n; i++) - { - ref Vector128 c0 = ref Unsafe.Add(ref c0Base, i); - c0 = AdvSimd.Multiply(c0, scale); - } - } - - /// - public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) - { - ref Vector128 destLuminance = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - - ref Vector128 srcRed = - ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); - ref Vector128 srcGreen = - ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); - ref Vector128 srcBlue = - ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); - - // Used for the color conversion - var f0299 = Vector128.Create(0.299f); - var f0587 = Vector128.Create(0.587f); - var f0114 = Vector128.Create(0.114f); - - nuint n = values.Component0.Vector128Count(); - for (nuint i = 0; i < n; i++) - { - ref Vector128 r = ref Unsafe.Add(ref srcRed, i); - ref Vector128 g = ref Unsafe.Add(ref srcGreen, i); - ref Vector128 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(AdvSimd.Multiply(f0114, b), f0587, g), f0299, r); - } - } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs deleted file mode 100644 index a9e1c5d13..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -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; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class GrayscaleAvx : JpegColorConverterAvx - { - public GrayscaleAvx(int precision) - : base(JpegColorSpace.Grayscale, precision) - { - } - - /// - public override void ConvertToRgbInplace(in ComponentValues values) - { - ref Vector256 c0Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - - // Used for the color conversion - var scale = Vector256.Create(1 / this.MaximumValue); - - nuint n = values.Component0.Vector256Count(); - for (nuint i = 0; i < n; i++) - { - ref Vector256 c0 = ref Unsafe.Add(ref c0Base, i); - c0 = Avx.Multiply(c0, scale); - } - } - - /// - public override void ConvertFromRgb(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); - - nuint n = values.Component0.Vector256Count(); - for (nuint 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.GrayScaleScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs index 4d3b5c60b..c01811182 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs @@ -16,7 +16,7 @@ internal abstract partial class JpegColorConverterBase } /// - public override void ConvertToRgbInplace(in ComponentValues values) + public override void ConvertToRgbInPlace(in ComponentValues values) => ConvertToRgbInplace(values.Component0, this.MaximumValue); /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs deleted file mode 100644 index 75eeb17dd..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.Arm; -using System.Runtime.Intrinsics.X86; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class RgbArm : JpegColorConverterArm - { - public RgbArm(int precision) - : base(JpegColorSpace.RGB, precision) - { - } - - /// - public override void ConvertToRgbInplace(in ComponentValues values) - { - ref Vector128 rBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector128 gBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector128 bBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - - // Used for the color conversion - var scale = Vector128.Create(1 / this.MaximumValue); - nuint n = values.Component0.Vector128Count(); - for (nuint i = 0; i < n; i++) - { - ref Vector128 r = ref Unsafe.Add(ref rBase, i); - ref Vector128 g = ref Unsafe.Add(ref gBase, i); - ref Vector128 b = ref Unsafe.Add(ref bBase, i); - r = AdvSimd.Multiply(r, scale); - g = AdvSimd.Multiply(g, scale); - b = AdvSimd.Multiply(b, scale); - } - } - - /// - public override void ConvertFromRgb(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.RgbAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs deleted file mode 100644 index b56728fa7..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class RgbAvx : JpegColorConverterAvx - { - public RgbAvx(int precision) - : base(JpegColorSpace.RGB, precision) - { - } - - /// - public override void ConvertToRgbInplace(in ComponentValues values) - { - ref Vector256 rBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector256 gBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector256 bBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - - // Used for the color conversion - var scale = Vector256.Create(1 / this.MaximumValue); - nuint n = values.Component0.Vector256Count(); - for (nuint i = 0; i < n; i++) - { - ref Vector256 r = ref Unsafe.Add(ref rBase, i); - ref Vector256 g = ref Unsafe.Add(ref gBase, i); - ref Vector256 b = ref Unsafe.Add(ref bBase, i); - r = Avx.Multiply(r, scale); - g = Avx.Multiply(g, scale); - b = Avx.Multiply(b, scale); - } - } - - /// - public override void ConvertFromRgb(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.RgbScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs index a43b7ef84..f62b2c7ff 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs @@ -13,7 +13,7 @@ internal abstract partial class JpegColorConverterBase } /// - public override void ConvertToRgbInplace(in ComponentValues values) + public override void ConvertToRgbInPlace(in ComponentValues values) => ConvertToRgbInplace(values, this.MaximumValue); /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrArm.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrArm.cs deleted file mode 100644 index 4f7cf1ed6..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrArm.cs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.Arm; -using System.Runtime.Intrinsics.X86; -using static SixLabors.ImageSharp.SimdUtils; - -// ReSharper disable ImpureMethodCallOnReadonlyValueField -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class YCbCrArm : JpegColorConverterArm - { - public YCbCrArm(int precision) - : base(JpegColorSpace.YCbCr, precision) - { - } - - /// - public override void ConvertToRgbInplace(in ComponentValues values) - { - ref Vector128 c0Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector128 c1Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector128 c2Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - - // Used for the color conversion - var chromaOffset = Vector128.Create(-this.HalfValue); - var scale = Vector128.Create(1 / this.MaximumValue); - var rCrMult = Vector128.Create(YCbCrScalar.RCrMult); - var gCbMult = Vector128.Create(-YCbCrScalar.GCbMult); - var gCrMult = Vector128.Create(-YCbCrScalar.GCrMult); - var bCbMult = Vector128.Create(YCbCrScalar.BCbMult); - - // Walking 8 elements at one step: - nuint n = (uint)values.Component0.Length / (uint)Vector128.Count; - for (nuint i = 0; i < n; i++) - { - // y = yVals[i]; - // cb = cbVals[i] - 128F; - // cr = crVals[i] - 128F; - ref Vector128 c0 = ref Unsafe.Add(ref c0Base, i); - ref Vector128 c1 = ref Unsafe.Add(ref c1Base, i); - ref Vector128 c2 = ref Unsafe.Add(ref c2Base, i); - - Vector128 y = c0; - Vector128 cb = AdvSimd.Add(c1, chromaOffset); - Vector128 cr = AdvSimd.Add(c2, chromaOffset); - - // r = y + (1.402F * cr); - // g = y - (0.344136F * cb) - (0.714136F * cr); - // b = y + (1.772F * cb); - Vector128 r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); - Vector128 g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); - Vector128 b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); - - r = AdvSimd.Multiply(AdvSimd.RoundToNearest(r), scale); - g = AdvSimd.Multiply(AdvSimd.RoundToNearest(g), scale); - b = AdvSimd.Multiply(AdvSimd.RoundToNearest(b), scale); - - c0 = r; - c1 = g; - c2 = b; - } - } - - /// - public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) - { - ref Vector128 destY = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector128 destCb = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector128 destCr = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - - ref Vector128 srcR = - ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); - ref Vector128 srcG = - ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); - ref Vector128 srcB = - ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); - - // Used for the color conversion - var chromaOffset = Vector128.Create(this.HalfValue); - - var f0299 = Vector128.Create(0.299f); - var f0587 = Vector128.Create(0.587f); - var f0114 = Vector128.Create(0.114f); - var fn0168736 = Vector128.Create(-0.168736f); - var fn0331264 = Vector128.Create(-0.331264f); - var fn0418688 = Vector128.Create(-0.418688f); - var fn0081312F = Vector128.Create(-0.081312F); - var f05 = Vector128.Create(0.5f); - - nuint n = (uint)values.Component0.Length / (uint)Vector128.Count; - for (nuint i = 0; i < n; i++) - { - Vector128 r = Unsafe.Add(ref srcR, i); - Vector128 g = Unsafe.Add(ref srcG, i); - Vector128 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) - Vector128 y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f0114, b), f0587, g), f0299, r); - Vector128 cb = AdvSimd.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f05, b), fn0331264, g), fn0168736, r)); - Vector128 cr = AdvSimd.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(fn0081312F, b), fn0418688, g), f05, r)); - - 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.YCbCrAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs deleted file mode 100644 index c5fa786e2..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; -using static SixLabors.ImageSharp.SimdUtils; - -// ReSharper disable ImpureMethodCallOnReadonlyValueField -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class YCbCrAvx : JpegColorConverterAvx - { - public YCbCrAvx(int precision) - : base(JpegColorSpace.YCbCr, precision) - { - } - - /// - public override void ConvertToRgbInplace(in ComponentValues values) - { - 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)); - - // Used for the color conversion - var chromaOffset = Vector256.Create(-this.HalfValue); - var scale = Vector256.Create(1 / this.MaximumValue); - var rCrMult = Vector256.Create(YCbCrScalar.RCrMult); - var gCbMult = Vector256.Create(-YCbCrScalar.GCbMult); - var gCrMult = Vector256.Create(-YCbCrScalar.GCrMult); - var bCbMult = Vector256.Create(YCbCrScalar.BCbMult); - - // Walking 8 elements at one step: - nuint n = values.Component0.Vector256Count(); - for (nuint i = 0; i < n; i++) - { - // y = yVals[i]; - // cb = cbVals[i] - 128F; - // cr = crVals[i] - 128F; - 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 y = c0; - Vector256 cb = Avx.Add(c1, chromaOffset); - Vector256 cr = Avx.Add(c2, chromaOffset); - - // r = y + (1.402F * cr); - // g = y - (0.344136F * cb) - (0.714136F * cr); - // b = y + (1.772F * cb); - Vector256 r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); - Vector256 g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); - Vector256 b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); - - r = Avx.Multiply(Avx.RoundToNearestInteger(r), scale); - g = Avx.Multiply(Avx.RoundToNearestInteger(g), scale); - b = Avx.Multiply(Avx.RoundToNearestInteger(b), scale); - - c0 = r; - c1 = g; - c2 = b; - } - } - - /// - public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) - { - ref Vector256 destY = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector256 destCb = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - 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); - - var f0299 = Vector256.Create(0.299f); - var f0587 = Vector256.Create(0.587f); - var f0114 = Vector256.Create(0.114f); - var fn0168736 = Vector256.Create(-0.168736f); - var fn0331264 = Vector256.Create(-0.331264f); - var fn0418688 = Vector256.Create(-0.418688f); - var fn0081312F = Vector256.Create(-0.081312F); - var f05 = Vector256.Create(0.5f); - - nuint n = values.Component0.Vector256Count(); - for (nuint i = 0; i < n; i++) - { - 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) - // cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b) - Vector256 y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); - 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)); - - 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.YCbCrScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs index e3e5a452a..e7e29a87a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs @@ -19,7 +19,7 @@ internal abstract partial class JpegColorConverterBase } /// - public override void ConvertToRgbInplace(in ComponentValues values) + public override void ConvertToRgbInPlace(in ComponentValues values) => ConvertToRgbInplace(values, this.MaximumValue, this.HalfValue); /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs index a5d0c889e..2591ae310 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs @@ -27,13 +27,13 @@ internal abstract partial class JpegColorConverterBase ref Vector c2Base = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - var chromaOffset = new Vector(-this.HalfValue); + Vector chromaOffset = new(-this.HalfValue); - var scale = new Vector(1 / this.MaximumValue); - var rCrMult = new Vector(YCbCrScalar.RCrMult); - var gCbMult = new Vector(-YCbCrScalar.GCbMult); - var gCrMult = new Vector(-YCbCrScalar.GCrMult); - var bCbMult = new Vector(YCbCrScalar.BCbMult); + Vector scale = new(1 / this.MaximumValue); + Vector rCrMult = new(YCbCrScalar.RCrMult); + Vector gCbMult = new(-YCbCrScalar.GCbMult); + Vector gCrMult = new(-YCbCrScalar.GCrMult); + Vector bCbMult = new(YCbCrScalar.BCbMult); nuint n = values.Component0.VectorCount(); for (nuint i = 0; i < n; i++) @@ -89,19 +89,19 @@ internal abstract partial class JpegColorConverterBase ref Vector srcB = ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); - var chromaOffset = new Vector(this.HalfValue); + Vector chromaOffset = new(this.HalfValue); - var rYMult = new Vector(0.299f); - var gYMult = new Vector(0.587f); - var bYMult = new Vector(0.114f); + Vector rYMult = new(0.299f); + Vector gYMult = new(0.587f); + Vector bYMult = new(0.114f); - var rCbMult = new Vector(0.168736f); - var gCbMult = new Vector(0.331264f); - var bCbMult = new Vector(0.5f); + Vector rCbMult = new(0.168736f); + Vector gCbMult = new(0.331264f); + Vector bCbMult = new(0.5f); - var rCrMult = new Vector(0.5f); - var gCrMult = new Vector(0.418688f); - var bCrMult = new Vector(0.081312f); + Vector rCrMult = new(0.5f); + Vector gCrMult = new(0.418688f); + Vector bCrMult = new(0.081312f); nuint n = values.Component0.VectorCount(); for (nuint i = 0; i < n; i++) @@ -120,7 +120,7 @@ internal abstract partial class JpegColorConverterBase } /// - protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span r, Span g, Span b) - => YCbCrScalar.ConvertFromRgb(values, this.HalfValue, r, g, b); + protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => YCbCrScalar.ConvertFromRgb(values, this.HalfValue, rLane, gLane, bLane); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKArm64.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKArm64.cs deleted file mode 100644 index 285ba62cf..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKArm64.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.Arm; -using System.Runtime.Intrinsics.X86; -using static SixLabors.ImageSharp.SimdUtils; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class YccKArm64 : JpegColorConverterArm64 - { - public YccKArm64(int precision) - : base(JpegColorSpace.Ycck, precision) - { - } - - /// - public override void ConvertToRgbInplace(in ComponentValues values) - { - ref Vector128 c0Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector128 c1Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector128 c2Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - ref Vector128 kBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); - - // Used for the color conversion - var chromaOffset = Vector128.Create(-this.HalfValue); - var scale = Vector128.Create(1 / (this.MaximumValue * this.MaximumValue)); - var max = Vector128.Create(this.MaximumValue); - var rCrMult = Vector128.Create(YCbCrScalar.RCrMult); - var gCbMult = Vector128.Create(-YCbCrScalar.GCbMult); - var gCrMult = Vector128.Create(-YCbCrScalar.GCrMult); - var bCbMult = Vector128.Create(YCbCrScalar.BCbMult); - - // Walking 8 elements at one step: - nuint n = (uint)values.Component0.Length / (uint)Vector128.Count; - for (nuint i = 0; i < n; i++) - { - // y = yVals[i]; - // cb = cbVals[i] - 128F; - // cr = crVals[i] - 128F; - // k = kVals[i] / 256F; - ref Vector128 c0 = ref Unsafe.Add(ref c0Base, i); - ref Vector128 c1 = ref Unsafe.Add(ref c1Base, i); - ref Vector128 c2 = ref Unsafe.Add(ref c2Base, i); - Vector128 y = c0; - Vector128 cb = AdvSimd.Add(c1, chromaOffset); - Vector128 cr = AdvSimd.Add(c2, chromaOffset); - Vector128 scaledK = AdvSimd.Multiply(Unsafe.Add(ref kBase, i), scale); - - // r = y + (1.402F * cr); - // g = y - (0.344136F * cb) - (0.714136F * cr); - // b = y + (1.772F * cb); - Vector128 r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); - Vector128 g = - HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); - Vector128 b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); - - r = AdvSimd.Subtract(max, AdvSimd.RoundToNearest(r)); - g = AdvSimd.Subtract(max, AdvSimd.RoundToNearest(g)); - b = AdvSimd.Subtract(max, AdvSimd.RoundToNearest(b)); - - r = AdvSimd.Multiply(r, scaledK); - g = AdvSimd.Multiply(g, scaledK); - b = AdvSimd.Multiply(b, scaledK); - - c0 = r; - c1 = g; - c2 = b; - } - } - - /// - public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) - { - // rgb -> cmyk - CmykArm64.ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); - - // cmyk -> ycck - ref Vector128 destY = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector128 destCb = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector128 destCr = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - - ref Vector128 srcR = ref destY; - ref Vector128 srcG = ref destCb; - ref Vector128 srcB = ref destCr; - - // Used for the color conversion - var maxSampleValue = Vector128.Create(this.MaximumValue); - - var chromaOffset = Vector128.Create(this.HalfValue); - - var f0299 = Vector128.Create(0.299f); - var f0587 = Vector128.Create(0.587f); - var f0114 = Vector128.Create(0.114f); - var fn0168736 = Vector128.Create(-0.168736f); - var fn0331264 = Vector128.Create(-0.331264f); - var fn0418688 = Vector128.Create(-0.418688f); - var fn0081312F = Vector128.Create(-0.081312F); - var f05 = Vector128.Create(0.5f); - - nuint n = (uint)values.Component0.Length / (uint)Vector128.Count; - for (nuint i = 0; i < n; i++) - { - Vector128 r = AdvSimd.Subtract(maxSampleValue, Unsafe.Add(ref srcR, i)); - Vector128 g = AdvSimd.Subtract(maxSampleValue, Unsafe.Add(ref srcG, i)); - Vector128 b = AdvSimd.Subtract(maxSampleValue, 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) - Vector128 y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f0114, b), f0587, g), f0299, r); - Vector128 cb = AdvSimd.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f05, b), fn0331264, g), fn0168736, r)); - Vector128 cr = AdvSimd.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(fn0081312F, b), fn0418688, g), f05, r)); - - 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.YccKAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs deleted file mode 100644 index efe40ba08..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -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; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class YccKAvx : JpegColorConverterAvx - { - public YccKAvx(int precision) - : base(JpegColorSpace.Ycck, precision) - { - } - - /// - public override void ConvertToRgbInplace(in ComponentValues values) - { - 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 kBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); - - // Used for the color conversion - var chromaOffset = Vector256.Create(-this.HalfValue); - var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue)); - var max = Vector256.Create(this.MaximumValue); - var rCrMult = Vector256.Create(YCbCrScalar.RCrMult); - var gCbMult = Vector256.Create(-YCbCrScalar.GCbMult); - var gCrMult = Vector256.Create(-YCbCrScalar.GCrMult); - var bCbMult = Vector256.Create(YCbCrScalar.BCbMult); - - // Walking 8 elements at one step: - nuint n = values.Component0.Vector256Count(); - for (nuint i = 0; i < n; i++) - { - // y = yVals[i]; - // cb = cbVals[i] - 128F; - // cr = crVals[i] - 128F; - // k = kVals[i] / 256F; - 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 y = c0; - Vector256 cb = Avx.Add(c1, chromaOffset); - Vector256 cr = Avx.Add(c2, chromaOffset); - Vector256 scaledK = Avx.Multiply(Unsafe.Add(ref kBase, i), scale); - - // r = y + (1.402F * cr); - // g = y - (0.344136F * cb) - (0.714136F * cr); - // b = y + (1.772F * cb); - Vector256 r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); - Vector256 g = - HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); - Vector256 b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); - - r = Avx.Subtract(max, Avx.RoundToNearestInteger(r)); - g = Avx.Subtract(max, Avx.RoundToNearestInteger(g)); - b = Avx.Subtract(max, Avx.RoundToNearestInteger(b)); - - r = Avx.Multiply(r, scaledK); - g = Avx.Multiply(g, scaledK); - b = Avx.Multiply(b, scaledK); - - c0 = r; - c1 = g; - c2 = b; - } - } - - /// - public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) - { - // rgb -> cmyk - CmykAvx.ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); - - // cmyk -> ycck - ref Vector256 destY = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector256 destCb = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector256 destCr = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - - ref Vector256 srcR = ref destY; - ref Vector256 srcG = ref destCb; - ref Vector256 srcB = ref destCr; - - // Used for the color conversion - var maxSampleValue = Vector256.Create(this.MaximumValue); - - var chromaOffset = Vector256.Create(this.HalfValue); - - var f0299 = Vector256.Create(0.299f); - var f0587 = Vector256.Create(0.587f); - var f0114 = Vector256.Create(0.114f); - var fn0168736 = Vector256.Create(-0.168736f); - var fn0331264 = Vector256.Create(-0.331264f); - var fn0418688 = Vector256.Create(-0.418688f); - var fn0081312F = Vector256.Create(-0.081312F); - var f05 = Vector256.Create(0.5f); - - nuint n = values.Component0.Vector256Count(); - for (nuint i = 0; i < n; i++) - { - Vector256 r = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcR, i)); - Vector256 g = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcG, i)); - Vector256 b = Avx.Subtract(maxSampleValue, 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) - Vector256 y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); - 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)); - - 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.YccKScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs index e47572b02..610c0a84e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs @@ -19,7 +19,7 @@ internal abstract partial class JpegColorConverterBase } /// - public override void ConvertToRgbInplace(in ComponentValues values) + public override void ConvertToRgbInPlace(in ComponentValues values) => ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue); /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs deleted file mode 100644 index bfbbf200c..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.Arm; -using System.Runtime.Intrinsics.X86; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - /// - /// abstract base for implementations - /// based on instructions. - /// - /// - /// Converters of this family would expect input buffers lengths to be - /// divisible by 8 without a remainder. - /// This is guaranteed by real-life data as jpeg stores pixels via 8x8 blocks. - /// DO NOT pass test data of invalid size to these converters as they - /// potentially won't do a bound check and return a false positive result. - /// - internal abstract class JpegColorConverterArm : JpegColorConverterBase - { - protected JpegColorConverterArm(JpegColorSpace colorSpace, int precision) - : base(colorSpace, precision) - { - } - - public static bool IsSupported => AdvSimd.IsSupported; - - public sealed override bool IsAvailable => IsSupported; - - public sealed override int ElementsPerBatch => Vector128.Count; - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm64.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm64.cs deleted file mode 100644 index d6d4d6ef9..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm64.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.Arm; -using System.Runtime.Intrinsics.X86; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - /// - /// abstract base for implementations - /// based on instructions. - /// - /// - /// Converters of this family would expect input buffers lengths to be - /// divisible by 8 without a remainder. - /// This is guaranteed by real-life data as jpeg stores pixels via 8x8 blocks. - /// DO NOT pass test data of invalid size to these converters as they - /// potentially won't do a bound check and return a false positive result. - /// - internal abstract class JpegColorConverterArm64 : JpegColorConverterBase - { - protected JpegColorConverterArm64(JpegColorSpace colorSpace, int precision) - : base(colorSpace, precision) - { - } - - public static bool IsSupported => AdvSimd.Arm64.IsSupported; - - public sealed override bool IsAvailable => IsSupported; - - public sealed override int ElementsPerBatch => Vector128.Count; - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterAvx.cs deleted file mode 100644 index a3e41bdc6..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterAvx.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - /// - /// abstract base for implementations - /// based on instructions. - /// - /// - /// Converters of this family would expect input buffers lengths to be - /// divisible by 8 without a remainder. - /// This is guaranteed by real-life data as jpeg stores pixels via 8x8 blocks. - /// DO NOT pass test data of invalid size to these converters as they - /// potentially won't do a bound check and return a false positive result. - /// - internal abstract class JpegColorConverterAvx : JpegColorConverterBase - { - protected JpegColorConverterAvx(JpegColorSpace colorSpace, int precision) - : base(colorSpace, precision) - { - } - - public static bool IsSupported => Avx.IsSupported; - - public sealed override bool IsAvailable => IsSupported; - - public sealed override int ElementsPerBatch => Vector256.Count; - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs index 041f6b057..581919e72 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs @@ -71,25 +71,14 @@ internal abstract partial class JpegColorConverterBase /// The precision in bits. /// Invalid colorspace. public static JpegColorConverterBase GetConverter(JpegColorSpace colorSpace, int precision) - { - JpegColorConverterBase converter = Array.Find( - Converters, - c => c.ColorSpace == colorSpace - && c.Precision == precision); - - if (converter is null) - { - throw new InvalidImageContentException($"Could not find any converter for JpegColorSpace {colorSpace}!"); - } - - return converter; - } + => Array.Find(Converters, c => c.ColorSpace == colorSpace && c.Precision == precision) + ?? throw new InvalidImageContentException($"Could not find any converter for JpegColorSpace {colorSpace}!"); /// - /// Converts planar jpeg component values in to RGB color space inplace. + /// Converts planar jpeg component values in to RGB color space in-place. /// - /// The input/ouptut as a stack-only struct - public abstract void ConvertToRgbInplace(in ComponentValues values); + /// The input/output as a stack-only struct + public abstract void ConvertToRgbInPlace(in ComponentValues values); /// /// Converts RGB lanes to jpeg component values. @@ -101,31 +90,25 @@ internal abstract partial class JpegColorConverterBase public abstract void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane); /// - /// Returns the s for all supported colorspaces and precisions. + /// Returns the s for all supported color spaces and precisions. /// private static JpegColorConverterBase[] CreateConverters() - { - // 5 color types with 2 supported precisions: 8 bit & 12 bit - const int colorConvertersCount = 5 * 2; - - JpegColorConverterBase[] converters = new JpegColorConverterBase[colorConvertersCount]; - - // 8-bit converters - converters[0] = GetYCbCrConverter(8); - converters[1] = GetYccKConverter(8); - converters[2] = GetCmykConverter(8); - converters[3] = GetGrayScaleConverter(8); - converters[4] = GetRgbConverter(8); - - // 12-bit converters - converters[5] = GetYCbCrConverter(12); - converters[6] = GetYccKConverter(12); - converters[7] = GetCmykConverter(12); - converters[8] = GetGrayScaleConverter(12); - converters[9] = GetRgbConverter(12); - - return converters; - } + => [ + + // 8-bit converters + GetYCbCrConverter(8), + GetYccKConverter(8), + GetCmykConverter(8), + GetGrayScaleConverter(8), + GetRgbConverter(8), + + // 12-bit converters + GetYCbCrConverter(12), + GetYccKConverter(12), + GetCmykConverter(12), + GetGrayScaleConverter(12), + GetRgbConverter(12), + ]; /// /// Returns the s for the YCbCr colorspace. @@ -133,16 +116,6 @@ internal abstract partial class JpegColorConverterBase /// The precision in bits. private static JpegColorConverterBase GetYCbCrConverter(int precision) { - if (JpegColorConverterAvx.IsSupported) - { - return new YCbCrAvx(precision); - } - - if (JpegColorConverterArm.IsSupported) - { - return new YCbCrArm(precision); - } - if (JpegColorConverterVector.IsSupported) { return new YCbCrVector(precision); @@ -157,16 +130,6 @@ internal abstract partial class JpegColorConverterBase /// The precision in bits. private static JpegColorConverterBase GetYccKConverter(int precision) { - if (JpegColorConverterAvx.IsSupported) - { - return new YccKAvx(precision); - } - - if (JpegColorConverterArm64.IsSupported) - { - return new YccKArm64(precision); - } - if (JpegColorConverterVector.IsSupported) { return new YccKVector(precision); @@ -181,16 +144,6 @@ internal abstract partial class JpegColorConverterBase /// The precision in bits. private static JpegColorConverterBase GetCmykConverter(int precision) { - if (JpegColorConverterAvx.IsSupported) - { - return new CmykAvx(precision); - } - - if (JpegColorConverterArm64.IsSupported) - { - return new CmykArm64(precision); - } - if (JpegColorConverterVector.IsSupported) { return new CmykVector(precision); @@ -205,16 +158,6 @@ internal abstract partial class JpegColorConverterBase /// The precision in bits. private static JpegColorConverterBase GetGrayScaleConverter(int precision) { - if (JpegColorConverterAvx.IsSupported) - { - return new GrayscaleAvx(precision); - } - - if (JpegColorConverterArm.IsSupported) - { - return new GrayscaleArm(precision); - } - if (JpegColorConverterVector.IsSupported) { return new GrayScaleVector(precision); @@ -229,16 +172,6 @@ internal abstract partial class JpegColorConverterBase /// The precision in bits. private static JpegColorConverterBase GetRgbConverter(int precision) { - if (JpegColorConverterAvx.IsSupported) - { - return new RgbAvx(precision); - } - - if (JpegColorConverterArm.IsSupported) - { - return new RgbArm(precision); - } - if (JpegColorConverterVector.IsSupported) { return new RgbVector(precision); @@ -295,7 +228,7 @@ internal abstract partial class JpegColorConverterBase // In case of grayscale, Component1 and Component2 point to Component0 memory area this.Component1 = this.ComponentCount > 1 ? componentBuffers[1].DangerousGetRowSpan(row) : this.Component0; this.Component2 = this.ComponentCount > 2 ? componentBuffers[2].DangerousGetRowSpan(row) : this.Component0; - this.Component3 = this.ComponentCount > 3 ? componentBuffers[3].DangerousGetRowSpan(row) : Span.Empty; + this.Component3 = this.ComponentCount > 3 ? componentBuffers[3].DangerousGetRowSpan(row) : []; } /// @@ -314,7 +247,7 @@ internal abstract partial class JpegColorConverterBase // In case of grayscale, Component1 and Component2 point to Component0 memory area this.Component1 = this.ComponentCount > 1 ? processors[1].GetColorBufferRowSpan(row) : this.Component0; this.Component2 = this.ComponentCount > 2 ? processors[2].GetColorBufferRowSpan(row) : this.Component0; - this.Component3 = this.ComponentCount > 3 ? processors[3].GetColorBufferRowSpan(row) : Span.Empty; + this.Component3 = this.ComponentCount > 3 ? processors[3].GetColorBufferRowSpan(row) : []; } /// @@ -333,7 +266,7 @@ internal abstract partial class JpegColorConverterBase // In case of grayscale, Component1 and Component2 point to Component0 memory area this.Component1 = this.ComponentCount > 1 ? processors[1].GetColorBufferRowSpan(row) : this.Component0; this.Component2 = this.ComponentCount > 2 ? processors[2].GetColorBufferRowSpan(row) : this.Component0; - this.Component3 = this.ComponentCount > 3 ? processors[3].GetColorBufferRowSpan(row) : Span.Empty; + this.Component3 = this.ComponentCount > 3 ? processors[3].GetColorBufferRowSpan(row) : []; } internal ComponentValues( @@ -353,9 +286,9 @@ internal abstract partial class JpegColorConverterBase public ComponentValues Slice(int start, int length) { Span c0 = this.Component0.Slice(start, length); - Span c1 = this.Component1.Length > 0 ? this.Component1.Slice(start, length) : Span.Empty; - Span c2 = this.Component2.Length > 0 ? this.Component2.Slice(start, length) : Span.Empty; - Span c3 = this.Component3.Length > 0 ? this.Component3.Slice(start, length) : Span.Empty; + Span c1 = this.Component1.Length > 0 ? this.Component1.Slice(start, length) : []; + Span c2 = this.Component2.Length > 0 ? this.Component2.Slice(start, length) : []; + Span c3 = this.Component3.Length > 0 ? this.Component3.Slice(start, length) : []; return new ComponentValues(this.ComponentCount, c0, c1, c2, c3); } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs index 5f9688a79..1c37f5eef 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs @@ -36,7 +36,7 @@ internal abstract partial class JpegColorConverterBase public override int ElementsPerBatch => Vector.Count; /// - public sealed override void ConvertToRgbInplace(in ComponentValues values) + public sealed override void ConvertToRgbInPlace(in ComponentValues values) { DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware."); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs index 7f89baba5..561d273e6 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs @@ -141,7 +141,7 @@ internal class SpectralConverter : SpectralConverter, IDisposable JpegColorConverterBase.ComponentValues values = new(this.componentProcessors, y); - this.colorConverter.ConvertToRgbInplace(values); + this.colorConverter.ConvertToRgbInPlace(values); values = values.Slice(0, width); // slice away Jpeg padding Span r = this.rgbBuffer.Slice(0, width); diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs index 9189bec37..e377a6f97 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs @@ -19,7 +19,7 @@ public class CmykColorConversion : ColorConversionBenchmark { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.CmykScalar(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.CmykScalar(8).ConvertToRgbInPlace(values); } [Benchmark] @@ -27,22 +27,6 @@ public class CmykColorConversion : ColorConversionBenchmark { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.CmykVector(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorAvx() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.CmykAvx(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorArm64() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.CmykArm64(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.CmykVector(8).ConvertToRgbInPlace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs index a1d85ef46..a63a97152 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs @@ -19,22 +19,14 @@ public class GrayscaleColorConversion : ColorConversionBenchmark { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.GrayscaleScalar(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.GrayscaleScalar(8).ConvertToRgbInPlace(values); } [Benchmark] - public void SimdVectorAvx() + public void SimdVector8() { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.GrayscaleAvx(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorArm() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.GrayscaleArm(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.GrayScaleVector(8).ConvertToRgbInPlace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs index 5e2b6fe86..62bf75d42 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs @@ -19,7 +19,7 @@ public class RgbColorConversion : ColorConversionBenchmark { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.RgbScalar(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.RgbScalar(8).ConvertToRgbInPlace(values); } [Benchmark] @@ -27,22 +27,6 @@ public class RgbColorConversion : ColorConversionBenchmark { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.RgbVector(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorAvx() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.RgbAvx(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorArm() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.RgbArm(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.RgbVector(8).ConvertToRgbInPlace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs index f8621c250..eb9224fee 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs @@ -19,7 +19,7 @@ public class YCbCrColorConversion : ColorConversionBenchmark { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.YCbCrScalar(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.YCbCrScalar(8).ConvertToRgbInPlace(values); } [Benchmark] @@ -27,22 +27,6 @@ public class YCbCrColorConversion : ColorConversionBenchmark { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.YCbCrVector(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorAvx() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YCbCrAvx(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorArm() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YCbCrArm(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.YCbCrVector(8).ConvertToRgbInPlace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs index a414b6ed4..1a81c2190 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs @@ -19,7 +19,7 @@ public class YccKColorConverter : ColorConversionBenchmark { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.YccKScalar(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.YccKScalar(8).ConvertToRgbInPlace(values); } [Benchmark] @@ -27,22 +27,6 @@ public class YccKColorConverter : ColorConversionBenchmark { var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - new JpegColorConverterBase.YccKVector(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorAvx2() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YccKAvx(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorArm64() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YccKArm64(8).ConvertToRgbInplace(values); + new JpegColorConverterBase.YccKVector(8).ConvertToRgbInPlace(values); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 7aabdaa58..02a5d8907 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -83,7 +83,7 @@ public class JpegColorConverterTests Type expectedType = typeof(JpegColorConverterBase.RgbScalar); if (Avx.IsSupported) { - expectedType = typeof(JpegColorConverterBase.RgbAvx); + expectedType = typeof(JpegColorConverterBase.RgbVector); } else if (Sse2.IsSupported) { @@ -91,7 +91,7 @@ public class JpegColorConverterTests } else if (AdvSimd.IsSupported) { - expectedType = typeof(JpegColorConverterBase.RgbArm); + expectedType = typeof(JpegColorConverterBase.RgbVector); } // act @@ -116,7 +116,7 @@ public class JpegColorConverterTests Type expectedType = typeof(JpegColorConverterBase.GrayscaleScalar); if (Avx.IsSupported) { - expectedType = typeof(JpegColorConverterBase.GrayscaleAvx); + expectedType = typeof(JpegColorConverterBase.GrayScaleVector); } else if (Sse2.IsSupported) { @@ -124,7 +124,7 @@ public class JpegColorConverterTests } else if (AdvSimd.IsSupported) { - expectedType = typeof(JpegColorConverterBase.GrayscaleArm); + expectedType = typeof(JpegColorConverterBase.GrayScaleVector); } // act @@ -149,7 +149,7 @@ public class JpegColorConverterTests Type expectedType = typeof(JpegColorConverterBase.CmykScalar); if (Avx.IsSupported) { - expectedType = typeof(JpegColorConverterBase.CmykAvx); + expectedType = typeof(JpegColorConverterBase.CmykVector); } else if (Sse2.IsSupported) { @@ -157,7 +157,7 @@ public class JpegColorConverterTests } else if (AdvSimd.Arm64.IsSupported) { - expectedType = typeof(JpegColorConverterBase.CmykArm64); + expectedType = typeof(JpegColorConverterBase.CmykVector); } // act @@ -182,7 +182,7 @@ public class JpegColorConverterTests Type expectedType = typeof(JpegColorConverterBase.YCbCrScalar); if (Avx.IsSupported) { - expectedType = typeof(JpegColorConverterBase.YCbCrAvx); + expectedType = typeof(JpegColorConverterBase.YCbCrVector); } else if (Sse2.IsSupported) { @@ -190,7 +190,7 @@ public class JpegColorConverterTests } else if (AdvSimd.IsSupported) { - expectedType = typeof(JpegColorConverterBase.YCbCrArm); + expectedType = typeof(JpegColorConverterBase.YCbCrVector); } // act @@ -215,7 +215,7 @@ public class JpegColorConverterTests Type expectedType = typeof(JpegColorConverterBase.YccKScalar); if (Avx.IsSupported) { - expectedType = typeof(JpegColorConverterBase.YccKAvx); + expectedType = typeof(JpegColorConverterBase.YccKVector); } else if (Sse2.IsSupported) { @@ -223,7 +223,7 @@ public class JpegColorConverterTests } else if (AdvSimd.Arm64.IsSupported) { - expectedType = typeof(JpegColorConverterBase.YccKArm64); + expectedType = typeof(JpegColorConverterBase.YccKVector); } // act @@ -405,176 +405,6 @@ public class JpegColorConverterTests new JpegColorConverterBase.YccKScalar(8)); } - [Theory] - [MemberData(nameof(Seeds))] - public void FromYCbCrAvx2(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.YCbCrAvx(8), - 3, - seed, - new JpegColorConverterBase.YCbCrScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToYCbCrAvx2(int seed) => - this.TestConversionFromRgb( - new JpegColorConverterBase.YCbCrAvx(8), - 3, - seed, - new JpegColorConverterBase.YCbCrScalar(8), - precísion: 2); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromYCbCrArm(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.YCbCrArm(8), - 3, - seed, - new JpegColorConverterBase.YCbCrScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToYCbCrArm(int seed) => - this.TestConversionFromRgb( - new JpegColorConverterBase.YCbCrArm(8), - 3, - seed, - new JpegColorConverterBase.YCbCrScalar(8), - precísion: 2); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromCmykAvx2(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.CmykAvx(8), - 4, - seed, - new JpegColorConverterBase.CmykScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToCmykAvx2(int seed) => - this.TestConversionFromRgb( - new JpegColorConverterBase.CmykAvx(8), - 4, - seed, - new JpegColorConverterBase.CmykScalar(8), - precísion: 4); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromCmykArm(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.CmykArm64(8), - 4, - seed, - new JpegColorConverterBase.CmykScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToCmykArm(int seed) => - this.TestConversionFromRgb( - new JpegColorConverterBase.CmykArm64(8), - 4, - seed, - new JpegColorConverterBase.CmykScalar(8), - precísion: 4); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromGrayscaleAvx2(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.GrayscaleAvx(8), - 1, - seed, - new JpegColorConverterBase.GrayscaleScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToGrayscaleAvx2(int seed) => - this.TestConversionFromRgb( - new JpegColorConverterBase.GrayscaleAvx(8), - 1, - seed, - new JpegColorConverterBase.GrayscaleScalar(8), - precísion: 3); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromGrayscaleArm(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.GrayscaleArm(8), - 1, - seed, - new JpegColorConverterBase.GrayscaleScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToGrayscaleArm(int seed) => - this.TestConversionFromRgb( - new JpegColorConverterBase.GrayscaleArm(8), - 1, - seed, - new JpegColorConverterBase.GrayscaleScalar(8), - precísion: 3); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbAvx2(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.RgbAvx(8), - 3, - seed, - new JpegColorConverterBase.RgbScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbArm(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.RgbArm(8), - 3, - seed, - new JpegColorConverterBase.RgbScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromYccKAvx2(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.YccKAvx(8), - 4, - seed, - new JpegColorConverterBase.YccKScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToYccKAvx2(int seed) => - this.TestConversionFromRgb( - new JpegColorConverterBase.YccKAvx(8), - 4, - seed, - new JpegColorConverterBase.YccKScalar(8), - precísion: 4); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromYccKArm64(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.YccKArm64(8), - 4, - seed, - new JpegColorConverterBase.YccKScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToYccKArm64(int seed) => - this.TestConversionFromRgb( - new JpegColorConverterBase.YccKArm64(8), - 4, - seed, - new JpegColorConverterBase.YccKScalar(8), - precísion: 4); - private void TestConversionToRgb( JpegColorConverterBase converter, int componentCount, @@ -595,28 +425,6 @@ public class JpegColorConverterTests baseLineConverter); } - private void TestConversionFromRgb( - JpegColorConverterBase converter, - int componentCount, - int seed, - JpegColorConverterBase baseLineConverter, - int precísion) - { - if (!converter.IsAvailable) - { - this.Output.WriteLine( - $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); - return; - } - - ValidateConversionFromRgb( - converter, - componentCount, - seed, - baseLineConverter, - precísion); - } - private static JpegColorConverterBase.ComponentValues CreateRandomValues( int length, int componentCount, @@ -669,7 +477,7 @@ public class JpegColorConverterTests original.Component2.ToArray(), original.Component3.ToArray()); - converter.ConvertToRgbInplace(actual); + converter.ConvertToRgbInPlace(actual); for (int i = 0; i < TestBufferLength; i++) { @@ -685,7 +493,7 @@ public class JpegColorConverterTests original.Component1.ToArray(), original.Component2.ToArray(), original.Component3.ToArray()); - baseLineConverter.ConvertToRgbInplace(expected); + baseLineConverter.ConvertToRgbInPlace(expected); if (componentCount == 1) { Assert.True(expected.Component0.SequenceEqual(actual.Component0)); @@ -781,7 +589,7 @@ public class JpegColorConverterTests ValidateYCbCr(original, result, i); break; default: - Assert.True(false, $"Invalid Colorspace enum value: {colorSpace}."); + Assert.Fail($"Invalid Colorspace enum value: {colorSpace}."); break; } } From cd3aa18dd45848e46f733fcd052e57fb14585c69 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 May 2025 20:34:42 +1000 Subject: [PATCH 02/11] Rename and refactor method --- src/ImageSharp/Common/Helpers/SimdUtils.cs | 12 ++--- .../Formats/Jpeg/Components/Block8x8F.cs | 2 +- .../JpegColorConverter.YCbCrVector.cs | 6 +-- .../JpegColorConverter.YccKVector.cs | 6 +-- .../Jpeg/BlockOperations/Block8x8F_Round.cs | 48 +++++++++---------- .../ImageSharp.Tests/Common/SimdUtilsTests.cs | 4 +- 6 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs index 273947272..7f10f3182 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -38,7 +38,7 @@ internal static partial class SimdUtils /// /// The vector [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector FastRound(this Vector v) + internal static Vector RoundToNearestInteger(this Vector v) { if (Avx512F.IsSupported && Vector.Count == Vector512.Count) { @@ -59,13 +59,11 @@ internal static partial class SimdUtils } // https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L11 - Vector magic0 = new(int.MinValue); // 0x80000000 - Vector sgn0 = Vector.AsVectorSingle(magic0); - Vector and0 = Vector.BitwiseAnd(sgn0, v); - Vector or0 = Vector.BitwiseOr(and0, new Vector(8388608.0f)); - Vector add0 = Vector.Add(v, or0); + Vector sign = v & new Vector(-0F); + Vector val_2p23_f32 = sign | new Vector(8388608F); - return Vector.Subtract(add0, or0); + val_2p23_f32 = (v + val_2p23_f32) - val_2p23_f32; + return val_2p23_f32 | sign; } [Conditional("DEBUG")] diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index 018df5f9f..aa9249be5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -588,6 +588,6 @@ internal partial struct Block8x8F : IEquatable row += off; row = Vector.Max(row, Vector.Zero); row = Vector.Min(row, max); - return row.FastRound(); + return row.RoundToNearestInteger(); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs index 2591ae310..e3700b2ca 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs @@ -55,9 +55,9 @@ internal abstract partial class JpegColorConverterBase Vector g = y + (cb * gCbMult) + (cr * gCrMult); Vector b = y + (cb * bCbMult); - r = r.FastRound(); - g = g.FastRound(); - b = b.FastRound(); + r = r.RoundToNearestInteger(); + g = g.RoundToNearestInteger(); + b = b.RoundToNearestInteger(); r *= scale; g *= scale; b *= scale; diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs index 570a401ad..535e5f125 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs @@ -59,9 +59,9 @@ internal abstract partial class JpegColorConverterBase Vector g = y + (cb * gCbMult) + (cr * gCrMult); Vector b = y + (cb * bCbMult); - r = (max - r.FastRound()) * scaledK; - g = (max - g.FastRound()) * scaledK; - b = (max - b.FastRound()) * scaledK; + r = (max - r.RoundToNearestInteger()) * scaledK; + g = (max - g.RoundToNearestInteger()) * scaledK; + b = (max - b.RoundToNearestInteger()) * scaledK; c0 = r; c1 = g; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs index 1d8385168..92faa50d2 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs @@ -67,21 +67,21 @@ public unsafe class Block8x8F_Round ref Block8x8F b = ref this.block; ref Vector row0 = ref Unsafe.As>(ref b.V0L); - row0 = row0.FastRound(); + row0 = row0.RoundToNearestInteger(); ref Vector row1 = ref Unsafe.As>(ref b.V1L); - row1 = row1.FastRound(); + row1 = row1.RoundToNearestInteger(); ref Vector row2 = ref Unsafe.As>(ref b.V2L); - row2 = row2.FastRound(); + row2 = row2.RoundToNearestInteger(); ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row3 = row3.FastRound(); + row3 = row3.RoundToNearestInteger(); ref Vector row4 = ref Unsafe.As>(ref b.V4L); - row4 = row4.FastRound(); + row4 = row4.RoundToNearestInteger(); ref Vector row5 = ref Unsafe.As>(ref b.V5L); - row5 = row5.FastRound(); + row5 = row5.RoundToNearestInteger(); ref Vector row6 = ref Unsafe.As>(ref b.V6L); - row6 = row6.FastRound(); + row6 = row6.RoundToNearestInteger(); ref Vector row7 = ref Unsafe.As>(ref b.V7L); - row7 = row7.FastRound(); + row7 = row7.RoundToNearestInteger(); } [Benchmark] @@ -90,21 +90,21 @@ public unsafe class Block8x8F_Round ref Block8x8F b = ref Unsafe.AsRef(this.alignedPtr); ref Vector row0 = ref Unsafe.As>(ref b.V0L); - row0 = row0.FastRound(); + row0 = row0.RoundToNearestInteger(); ref Vector row1 = ref Unsafe.As>(ref b.V1L); - row1 = row1.FastRound(); + row1 = row1.RoundToNearestInteger(); ref Vector row2 = ref Unsafe.As>(ref b.V2L); - row2 = row2.FastRound(); + row2 = row2.RoundToNearestInteger(); ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row3 = row3.FastRound(); + row3 = row3.RoundToNearestInteger(); ref Vector row4 = ref Unsafe.As>(ref b.V4L); - row4 = row4.FastRound(); + row4 = row4.RoundToNearestInteger(); ref Vector row5 = ref Unsafe.As>(ref b.V5L); - row5 = row5.FastRound(); + row5 = row5.RoundToNearestInteger(); ref Vector row6 = ref Unsafe.As>(ref b.V6L); - row6 = row6.FastRound(); + row6 = row6.RoundToNearestInteger(); ref Vector row7 = ref Unsafe.As>(ref b.V7L); - row7 = row7.FastRound(); + row7 = row7.RoundToNearestInteger(); } [Benchmark] @@ -117,20 +117,20 @@ public unsafe class Block8x8F_Round ref Vector row2 = ref Unsafe.As>(ref b.V2L); ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row0 = row0.FastRound(); - row1 = row1.FastRound(); - row2 = row2.FastRound(); - row3 = row3.FastRound(); + row0 = row0.RoundToNearestInteger(); + row1 = row1.RoundToNearestInteger(); + row2 = row2.RoundToNearestInteger(); + row3 = row3.RoundToNearestInteger(); row0 = ref Unsafe.As>(ref b.V4L); row1 = ref Unsafe.As>(ref b.V5L); row2 = ref Unsafe.As>(ref b.V6L); row3 = ref Unsafe.As>(ref b.V7L); - row0 = row0.FastRound(); - row1 = row1.FastRound(); - row2 = row2.FastRound(); - row3 = row3.FastRound(); + row0 = row0.RoundToNearestInteger(); + row1 = row1.RoundToNearestInteger(); + row2 = row2.RoundToNearestInteger(); + row3 = row3.RoundToNearestInteger(); } [Benchmark] diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index 36b301264..77c9889ae 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -73,7 +73,7 @@ public partial class SimdUtilsTests public void FastRound() { Vector v = CreateExactTestVector1(); - Vector r = v.FastRound(); + Vector r = v.RoundToNearestInteger(); this.Output.WriteLine(r.ToString()); @@ -90,7 +90,7 @@ public partial class SimdUtilsTests public void FastRound_RandomValues(int seed, float scale) { Vector v = CreateRandomTestVector(seed, -scale * 0.5f, scale * 0.5f); - Vector r = v.FastRound(); + Vector r = v.RoundToNearestInteger(); this.Output.WriteLine(v.ToString()); this.Output.WriteLine(r.ToString()); From 959e6004d7b558ce31f41e286cbd740367f21592 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 May 2025 22:50:51 +1000 Subject: [PATCH 03/11] Begin re-implementing converters --- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 14 +- .../Common/Helpers/Vector128Utilities.cs | 61 ++++++++- .../Common/Helpers/Vector256Utilities.cs | 51 +++++++- .../Common/Helpers/Vector512Utilities.cs | 37 ++++++ .../JpegColorConverter.CmykVector.cs | 4 +- .../JpegColorConverter.GrayScaleVector.cs | 4 +- .../JpegColorConverter.RgbVector.cs | 4 +- .../JpegColorConverter.YCbCrVector.cs | 4 +- .../JpegColorConverter.YCbCrVector128.cs | 116 +++++++++++++++++ .../JpegColorConverter.YCbCrVector256.cs | 116 +++++++++++++++++ .../JpegColorConverter.YCbCrVector512.cs | 123 ++++++++++++++++++ .../JpegColorConverter.YccKVector.cs | 4 +- .../ColorConverters/JpegColorConverterBase.cs | 15 +++ .../JpegColorConverterVector.cs | 30 ++--- .../JpegColorConverterVector128.cs | 34 +++++ .../JpegColorConverterVector256.cs | 34 +++++ .../JpegColorConverterVector512.cs | 111 ++++++++++++++++ .../ColorConversion/YCbCrColorConversion.cs | 28 +++- .../Formats/Jpg/JpegColorConverterTests.cs | 13 +- 19 files changed, 768 insertions(+), 35 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector128.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector256.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector512.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector128.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector256.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector512.cs diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 17ccb396d..da93e1500 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -616,7 +616,12 @@ internal static partial class SimdUtils return Fma.MultiplyAdd(vm1, vm0, va); } - return Avx.Add(Avx.Multiply(vm0, vm1), va); + if (Avx.IsSupported) + { + return Avx.Add(Avx.Multiply(vm0, vm1), va); + } + + return va + (vm0 * vm1); } /// @@ -644,7 +649,12 @@ internal static partial class SimdUtils return AdvSimd.Add(AdvSimd.Multiply(vm0, vm1), va); } - return Sse.Add(Sse.Multiply(vm0, vm1), va); + if (Sse.IsSupported) + { + return Sse.Add(Sse.Multiply(vm0, vm1), va); + } + + return va + (vm0 * vm1); } /// diff --git a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs index b6dd319f0..8f4ea6292 100644 --- a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs @@ -193,13 +193,70 @@ internal static class Vector128Utilities return AdvSimd.ConvertToInt32RoundToEven(vector); } - Vector128 sign = vector & Vector128.Create(-0.0f); - Vector128 val_2p23_f32 = sign | Vector128.Create(8388608.0f); + Vector128 sign = vector & Vector128.Create(-0F); + Vector128 val_2p23_f32 = sign | Vector128.Create(8388608F); val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32; return Vector128.ConvertToInt32(val_2p23_f32 | sign); } + /// + /// Rounds all values in to the nearest integer + /// following semantics. + /// + /// The vector + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 RoundToNearestInteger(Vector128 vector) + { + if (Sse41.IsSupported) + { + return Sse41.RoundToNearestInteger(vector); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.RoundToNearest(vector); + } + + Vector128 sign = vector & Vector128.Create(-0F); + Vector128 val_2p23_f32 = sign | Vector128.Create(8388608F); + + val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32; + return val_2p23_f32 | sign; + } + + /// + /// Performs a multiplication and an addition of the . + /// + /// ret = (vm0 * vm1) + va + /// The vector to add to the intermediate result. + /// The first vector to multiply. + /// The second vector to multiply. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 MultiplyAdd( + Vector128 va, + Vector128 vm0, + Vector128 vm1) + { + if (Fma.IsSupported) + { + return Fma.MultiplyAdd(vm1, vm0, va); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.Add(AdvSimd.Multiply(vm0, vm1), va); + } + + if (Sse.IsSupported) + { + return Sse.Add(Sse.Multiply(vm0, vm1), va); + } + + return va + (vm0 * vm1); + } + /// /// Packs signed 16-bit integers to unsigned 8-bit integers and saturates. /// diff --git a/src/ImageSharp/Common/Helpers/Vector256Utilities.cs b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs index 6e8c0d1de..a7366f6da 100644 --- a/src/ImageSharp/Common/Helpers/Vector256Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs @@ -103,13 +103,60 @@ internal static class Vector256Utilities return Vector256.Create(lower, upper); } - Vector256 sign = vector & Vector256.Create(-0.0f); - Vector256 val_2p23_f32 = sign | Vector256.Create(8388608.0f); + Vector256 sign = vector & Vector256.Create(-0F); + Vector256 val_2p23_f32 = sign | Vector256.Create(8388608F); val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32; return Vector256.ConvertToInt32(val_2p23_f32 | sign); } + /// + /// Rounds all values in to the nearest integer + /// following semantics. + /// + /// The vector + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 RoundToNearestInteger(Vector256 vector) + { + if (Avx.IsSupported) + { + return Avx.RoundToNearestInteger(vector); + } + + Vector256 sign = vector & Vector256.Create(-0F); + Vector256 val_2p23_f32 = sign | Vector256.Create(8388608F); + + val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32; + return val_2p23_f32 | sign; + } + + /// + /// Performs a multiplication and an addition of the . + /// + /// ret = (vm0 * vm1) + va + /// The vector to add to the intermediate result. + /// The first vector to multiply. + /// The second vector to multiply. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyAdd( + Vector256 va, + Vector256 vm0, + Vector256 vm1) + { + if (Fma.IsSupported) + { + return Fma.MultiplyAdd(vm1, vm0, va); + } + + if (Avx.IsSupported) + { + return Avx.Add(Avx.Multiply(vm0, vm1), va); + } + + return va + (vm0 * vm1); + } + [DoesNotReturn] private static void ThrowUnreachableException() => throw new UnreachableException(); } diff --git a/src/ImageSharp/Common/Helpers/Vector512Utilities.cs b/src/ImageSharp/Common/Helpers/Vector512Utilities.cs index 0165af90e..77dc94475 100644 --- a/src/ImageSharp/Common/Helpers/Vector512Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector512Utilities.cs @@ -110,6 +110,43 @@ internal static class Vector512Utilities return Vector512.ConvertToInt32(val_2p23_f32 | sign); } + /// + /// Rounds all values in to the nearest integer + /// following semantics. + /// + /// The vector + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 RoundToNearestInteger(Vector512 vector) + { + if (Avx512F.IsSupported) + { + // imm8 = 0b1000: + // imm8[7:4] = 0b0000 -> preserve 0 fractional bits (round to whole numbers) + // imm8[3:0] = 0b1000 -> _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC (round to nearest even, suppress exceptions) + return Avx512F.RoundScale(vector, 0b0000_1000); + } + + Vector512 sign = vector & Vector512.Create(-0F); + Vector512 val_2p23_f32 = sign | Vector512.Create(8388608F); + + val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32; + return val_2p23_f32 | sign; + } + + /// + /// Performs a multiplication and an addition of the . + /// + /// ret = (vm0 * vm1) + va + /// The vector to add to the intermediate result. + /// The first vector to multiply. + /// The second vector to multiply. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyAdd( + Vector512 va, + Vector512 vm0, + Vector512 vm1) => va + (vm0 * vm1); + [DoesNotReturn] private static void ThrowUnreachableException() => throw new UnreachableException(); } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs index a59be009b..84b0b16d0 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs @@ -17,7 +17,7 @@ internal abstract partial class JpegColorConverterBase } /// - protected override void ConvertToRgbInplaceVectorized(in ComponentValues values) + protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) { ref Vector cBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); @@ -46,7 +46,7 @@ internal abstract partial class JpegColorConverterBase } /// - protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values) + protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) => CmykScalar.ConvertToRgbInplace(values, this.MaximumValue); /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs index cac10636f..b6283084f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs @@ -17,7 +17,7 @@ internal abstract partial class JpegColorConverterBase } /// - protected override void ConvertToRgbInplaceVectorized(in ComponentValues values) + protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) { ref Vector cBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); @@ -33,7 +33,7 @@ internal abstract partial class JpegColorConverterBase } /// - protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values) + protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) => GrayscaleScalar.ConvertToRgbInplace(values.Component0, this.MaximumValue); /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs index bd3142fa1..5fabc80c7 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs @@ -17,7 +17,7 @@ internal abstract partial class JpegColorConverterBase } /// - protected override void ConvertToRgbInplaceVectorized(in ComponentValues values) + protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) { ref Vector rBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); @@ -41,7 +41,7 @@ internal abstract partial class JpegColorConverterBase } /// - protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values) + protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) => RgbScalar.ConvertToRgbInplace(values, this.MaximumValue); /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs index e3700b2ca..92ff71b10 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs @@ -18,7 +18,7 @@ internal abstract partial class JpegColorConverterBase } /// - protected override void ConvertToRgbInplaceVectorized(in ComponentValues values) + protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) { ref Vector c0Base = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); @@ -69,7 +69,7 @@ internal abstract partial class JpegColorConverterBase } /// - protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values) + protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) => YCbCrScalar.ConvertToRgbInplace(values, this.MaximumValue, this.HalfValue); /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector128.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector128.cs new file mode 100644 index 000000000..95e3167cc --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector128.cs @@ -0,0 +1,116 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.Common.Helpers; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class YCbCrVector128 : JpegColorConverterVector128 + { + public YCbCrVector128(int precision) + : base(JpegColorSpace.YCbCr, precision) + { + } + + /// + public override void ConvertToRgbInPlace(in ComponentValues values) + { + ref Vector128 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 c1Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 c2Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + Vector128 chromaOffset = Vector128.Create(-this.HalfValue); + Vector128 scale = Vector128.Create(1 / this.MaximumValue); + Vector128 rCrMult = Vector128.Create(YCbCrScalar.RCrMult); + Vector128 gCbMult = Vector128.Create(-YCbCrScalar.GCbMult); + Vector128 gCrMult = Vector128.Create(-YCbCrScalar.GCrMult); + Vector128 bCbMult = Vector128.Create(YCbCrScalar.BCbMult); + + // Walking 8 elements at one step: + nuint n = values.Component0.Vector128Count(); + for (nuint i = 0; i < n; i++) + { + // y = yVals[i]; + // cb = cbVals[i] - 128F; + // cr = crVals[i] - 128F; + ref Vector128 c0 = ref Unsafe.Add(ref c0Base, i); + ref Vector128 c1 = ref Unsafe.Add(ref c1Base, i); + ref Vector128 c2 = ref Unsafe.Add(ref c2Base, i); + + Vector128 y = c0; + Vector128 cb = c1 + chromaOffset; + Vector128 cr = c2 + chromaOffset; + + // r = y + (1.402F * cr); + // g = y - (0.344136F * cb) - (0.714136F * cr); + // b = y + (1.772F * cb); + Vector128 r = Vector128Utilities.MultiplyAdd(y, cr, rCrMult); + Vector128 g = Vector128Utilities.MultiplyAdd(Vector128Utilities.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); + Vector128 b = Vector128Utilities.MultiplyAdd(y, cb, bCbMult); + + r = Vector128Utilities.RoundToNearestInteger(r) * scale; + g = Vector128Utilities.RoundToNearestInteger(g) * scale; + b = Vector128Utilities.RoundToNearestInteger(b) * scale; + + c0 = r; + c1 = g; + c2 = b; + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + ref Vector128 destY = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 destCb = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 destCr = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + ref Vector128 srcR = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector128 srcG = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector128 srcB = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + + Vector128 chromaOffset = Vector128.Create(this.HalfValue); + Vector128 f0299 = Vector128.Create(0.299f); + Vector128 f0587 = Vector128.Create(0.587f); + Vector128 f0114 = Vector128.Create(0.114f); + Vector128 fn0168736 = Vector128.Create(-0.168736f); + Vector128 fn0331264 = Vector128.Create(-0.331264f); + Vector128 fn0418688 = Vector128.Create(-0.418688f); + Vector128 fn0081312F = Vector128.Create(-0.081312F); + Vector128 f05 = Vector128.Create(0.5f); + + nuint n = values.Component0.Vector128Count(); + for (nuint i = 0; i < n; i++) + { + Vector128 r = Unsafe.Add(ref srcR, i); + Vector128 g = Unsafe.Add(ref srcG, i); + Vector128 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) + Vector128 y = Vector128Utilities.MultiplyAdd(Vector128Utilities.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + Vector128 cb = chromaOffset + Vector128Utilities.MultiplyAdd(Vector128Utilities.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r); + Vector128 cr = chromaOffset + Vector128Utilities.MultiplyAdd(Vector128Utilities.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r); + + 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.YCbCrVector256.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector256.cs new file mode 100644 index 000000000..8d8e23468 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector256.cs @@ -0,0 +1,116 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.Common.Helpers; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class YCbCrVector256 : JpegColorConverterVector256 + { + public YCbCrVector256(int precision) + : base(JpegColorSpace.YCbCr, precision) + { + } + + /// + public override void ConvertToRgbInPlace(in ComponentValues values) + { + 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)); + + Vector256 chromaOffset = Vector256.Create(-this.HalfValue); + Vector256 scale = Vector256.Create(1 / this.MaximumValue); + Vector256 rCrMult = Vector256.Create(YCbCrScalar.RCrMult); + Vector256 gCbMult = Vector256.Create(-YCbCrScalar.GCbMult); + Vector256 gCrMult = Vector256.Create(-YCbCrScalar.GCrMult); + Vector256 bCbMult = Vector256.Create(YCbCrScalar.BCbMult); + + // Walking 8 elements at one step: + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) + { + // y = yVals[i]; + // cb = cbVals[i] - 128F; + // cr = crVals[i] - 128F; + 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 y = c0; + Vector256 cb = c1 + chromaOffset; + Vector256 cr = c2 + chromaOffset; + + // r = y + (1.402F * cr); + // g = y - (0.344136F * cb) - (0.714136F * cr); + // b = y + (1.772F * cb); + Vector256 r = Vector256Utilities.MultiplyAdd(y, cr, rCrMult); + Vector256 g = Vector256Utilities.MultiplyAdd(Vector256Utilities.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); + Vector256 b = Vector256Utilities.MultiplyAdd(y, cb, bCbMult); + + r = Vector256Utilities.RoundToNearestInteger(r) * scale; + g = Vector256Utilities.RoundToNearestInteger(g) * scale; + b = Vector256Utilities.RoundToNearestInteger(b) * scale; + + c0 = r; + c1 = g; + c2 = b; + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + ref Vector256 destY = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector256 destCb = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + 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)); + + Vector256 chromaOffset = Vector256.Create(this.HalfValue); + Vector256 f0299 = Vector256.Create(0.299f); + Vector256 f0587 = Vector256.Create(0.587f); + Vector256 f0114 = Vector256.Create(0.114f); + Vector256 fn0168736 = Vector256.Create(-0.168736f); + Vector256 fn0331264 = Vector256.Create(-0.331264f); + Vector256 fn0418688 = Vector256.Create(-0.418688f); + Vector256 fn0081312F = Vector256.Create(-0.081312F); + Vector256 f05 = Vector256.Create(0.5f); + + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) + { + 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) + // cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b) + Vector256 y = Vector256Utilities.MultiplyAdd(Vector256Utilities.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + Vector256 cb = chromaOffset + Vector256Utilities.MultiplyAdd(Vector256Utilities.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r); + Vector256 cr = chromaOffset + Vector256Utilities.MultiplyAdd(Vector256Utilities.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r); + + 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.YCbCrVector512.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector512.cs new file mode 100644 index 000000000..79ad03512 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector512.cs @@ -0,0 +1,123 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.Common.Helpers; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class YCbCrVector512 : JpegColorConverterVector512 + { + public YCbCrVector512(int precision) + : base(JpegColorSpace.YCbCr, precision) + { + } + + /// + protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) + { + ref Vector512 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector512 c1Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector512 c2Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + Vector512 chromaOffset = Vector512.Create(-this.HalfValue); + Vector512 scale = Vector512.Create(1 / this.MaximumValue); + Vector512 rCrMult = Vector512.Create(YCbCrScalar.RCrMult); + Vector512 gCbMult = Vector512.Create(-YCbCrScalar.GCbMult); + Vector512 gCrMult = Vector512.Create(-YCbCrScalar.GCrMult); + Vector512 bCbMult = Vector512.Create(YCbCrScalar.BCbMult); + + nuint n = values.Component0.Vector512Count(); + for (nuint i = 0; i < n; i++) + { + // y = yVals[i]; + // cb = cbVals[i] - 128F; + // cr = crVals[i] - 128F; + ref Vector512 c0 = ref Unsafe.Add(ref c0Base, i); + ref Vector512 c1 = ref Unsafe.Add(ref c1Base, i); + ref Vector512 c2 = ref Unsafe.Add(ref c2Base, i); + + Vector512 y = c0; + Vector512 cb = c1 + chromaOffset; + Vector512 cr = c2 + chromaOffset; + + // r = y + (1.402F * cr); + // g = y - (0.344136F * cb) - (0.714136F * cr); + // b = y + (1.772F * cb); + Vector512 r = Vector512Utilities.MultiplyAdd(y, cr, rCrMult); + Vector512 g = Vector512Utilities.MultiplyAdd(Vector512Utilities.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); + Vector512 b = Vector512Utilities.MultiplyAdd(y, cb, bCbMult); + + r = Vector512Utilities.RoundToNearestInteger(r) * scale; + g = Vector512Utilities.RoundToNearestInteger(g) * scale; + b = Vector512Utilities.RoundToNearestInteger(b) * scale; + + c0 = r; + c1 = g; + c2 = b; + } + } + + /// + protected override void ConvertFromRgbVectorized(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + ref Vector512 destY = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector512 destCb = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector512 destCr = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + ref Vector512 srcR = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector512 srcG = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector512 srcB = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + + Vector512 chromaOffset = Vector512.Create(this.HalfValue); + Vector512 f0299 = Vector512.Create(0.299f); + Vector512 f0587 = Vector512.Create(0.587f); + Vector512 f0114 = Vector512.Create(0.114f); + Vector512 fn0168736 = Vector512.Create(-0.168736f); + Vector512 fn0331264 = Vector512.Create(-0.331264f); + Vector512 fn0418688 = Vector512.Create(-0.418688f); + Vector512 fn0081312F = Vector512.Create(-0.081312F); + Vector512 f05 = Vector512.Create(0.5f); + + nuint n = values.Component0.Vector512Count(); + for (nuint i = 0; i < n; i++) + { + Vector512 r = Unsafe.Add(ref srcR, i); + Vector512 g = Unsafe.Add(ref srcG, i); + Vector512 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) + Vector512 y = Vector512Utilities.MultiplyAdd(Vector512Utilities.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + Vector512 cb = chromaOffset + Vector512Utilities.MultiplyAdd(Vector512Utilities.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r); + Vector512 cr = chromaOffset + Vector512Utilities.MultiplyAdd(Vector512Utilities.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r); + + Unsafe.Add(ref destY, i) = y; + Unsafe.Add(ref destCb, i) = cb; + Unsafe.Add(ref destCr, i) = cr; + } + } + + /// + protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) + => YCbCrScalar.ConvertToRgbInplace(values, this.MaximumValue, this.HalfValue); + + /// + protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => YCbCrScalar.ConvertFromRgb(values, this.HalfValue, rLane, gLane, bLane); + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs index 535e5f125..5d57d0e54 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs @@ -17,7 +17,7 @@ internal abstract partial class JpegColorConverterBase } /// - protected override void ConvertToRgbInplaceVectorized(in ComponentValues values) + protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) { ref Vector c0Base = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); @@ -70,7 +70,7 @@ internal abstract partial class JpegColorConverterBase } /// - protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values) + protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) => YccKScalar.ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue); /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs index 581919e72..5681153a1 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs @@ -116,6 +116,21 @@ internal abstract partial class JpegColorConverterBase /// The precision in bits. private static JpegColorConverterBase GetYCbCrConverter(int precision) { + if (JpegColorConverterVector512.IsSupported) + { + return new YCbCrVector512(precision); + } + + if (JpegColorConverterVector256.IsSupported) + { + return new YCbCrVector256(precision); + } + + if (JpegColorConverterVector128.IsSupported) + { + return new YCbCrVector128(precision); + } + if (JpegColorConverterVector.IsSupported) { return new YCbCrVector(precision); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs index 1c37f5eef..f3c3eb8db 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs @@ -46,7 +46,7 @@ internal abstract partial class JpegColorConverterBase int simdCount = length - remainder; if (simdCount > 0) { - this.ConvertToRgbInplaceVectorized(values.Slice(0, simdCount)); + this.ConvertToRgbInPlaceVectorized(values.Slice(0, simdCount)); } // Jpeg images width is always divisible by 8 without a remainder @@ -56,12 +56,12 @@ internal abstract partial class JpegColorConverterBase // remainder pixels if (remainder > 0) { - this.ConvertToRgbInplaceScalarRemainder(values.Slice(simdCount, remainder)); + this.ConvertToRgbInPlaceScalarRemainder(values.Slice(simdCount, remainder)); } } /// - public sealed override void ConvertFromRgb(in ComponentValues values, Span r, Span g, Span b) + public sealed override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) { DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware."); @@ -73,9 +73,9 @@ internal abstract partial class JpegColorConverterBase { this.ConvertFromRgbVectorized( values.Slice(0, simdCount), - r.Slice(0, simdCount), - g.Slice(0, simdCount), - b.Slice(0, simdCount)); + rLane[..simdCount], + gLane[..simdCount], + bLane[..simdCount]); } // Jpeg images width is always divisible by 8 without a remainder @@ -87,25 +87,25 @@ internal abstract partial class JpegColorConverterBase { this.ConvertFromRgbScalarRemainder( values.Slice(simdCount, remainder), - r.Slice(simdCount, remainder), - g.Slice(simdCount, remainder), - b.Slice(simdCount, remainder)); + rLane.Slice(simdCount, remainder), + gLane.Slice(simdCount, remainder), + bLane.Slice(simdCount, remainder)); } } /// /// Converts planar jpeg component values in - /// to RGB color space inplace using API. + /// to RGB color space in place using API. /// - /// The input/ouptut as a stack-only struct - protected abstract void ConvertToRgbInplaceVectorized(in ComponentValues values); + /// The input/output as a stack-only struct + protected abstract void ConvertToRgbInPlaceVectorized(in ComponentValues values); /// /// Converts remainder of the planar jpeg component values after - /// conversion in . + /// conversion in . /// - /// The input/ouptut as a stack-only struct - protected abstract void ConvertToRgbInplaceScalarRemainder(in ComponentValues values); + /// The input/output as a stack-only struct + protected abstract void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values); /// /// Converts RGB lanes to jpeg component values using API. diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector128.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector128.cs new file mode 100644 index 000000000..35458a9a0 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector128.cs @@ -0,0 +1,34 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + /// + /// abstract base for implementations + /// based on instructions. + /// + /// + /// Converters of this family would expect input buffers lengths to be + /// divisible by 8 without a remainder. + /// This is guaranteed by real-life data as jpeg stores pixels via 8x8 blocks. + /// DO NOT pass test data of invalid size to these converters as they + /// potentially won't do a bound check and return a false positive result. + /// + internal abstract class JpegColorConverterVector128 : JpegColorConverterBase + { + protected JpegColorConverterVector128(JpegColorSpace colorSpace, int precision) + : base(colorSpace, precision) + { + } + + public static bool IsSupported => Vector128.IsHardwareAccelerated && Vector128.IsSupported; + + public sealed override bool IsAvailable => IsSupported; + + public sealed override int ElementsPerBatch => Vector128.Count; + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector256.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector256.cs new file mode 100644 index 000000000..14442383a --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector256.cs @@ -0,0 +1,34 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + /// + /// abstract base for implementations + /// based on instructions. + /// + /// + /// Converters of this family would expect input buffers lengths to be + /// divisible by 8 without a remainder. + /// This is guaranteed by real-life data as jpeg stores pixels via 8x8 blocks. + /// DO NOT pass test data of invalid size to these converters as they + /// potentially won't do a bound check and return a false positive result. + /// + internal abstract class JpegColorConverterVector256 : JpegColorConverterBase + { + protected JpegColorConverterVector256(JpegColorSpace colorSpace, int precision) + : base(colorSpace, precision) + { + } + + public static bool IsSupported => Vector256.IsHardwareAccelerated && Vector256.IsSupported; + + public sealed override bool IsAvailable => IsSupported; + + public sealed override int ElementsPerBatch => Vector256.Count; + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector512.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector512.cs new file mode 100644 index 000000000..e916ea5aa --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector512.cs @@ -0,0 +1,111 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + /// + /// abstract base for implementations + /// based on instructions. + /// + internal abstract class JpegColorConverterVector512 : JpegColorConverterBase + { + protected JpegColorConverterVector512(JpegColorSpace colorSpace, int precision) + : base(colorSpace, precision) + { + } + + public static bool IsSupported => Vector512.IsHardwareAccelerated && Vector512.IsSupported; + + /// + public override bool IsAvailable => IsSupported; + + /// + public override int ElementsPerBatch => Vector512.Count; + + /// + public sealed override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + 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)Vector512.Count); + + int simdCount = length - remainder; + if (simdCount > 0) + { + this.ConvertFromRgbVectorized( + values.Slice(0, simdCount), + rLane[..simdCount], + gLane[..simdCount], + bLane[..simdCount]); + } + + if (remainder > 0) + { + this.ConvertFromRgbScalarRemainder( + values.Slice(simdCount, remainder), + rLane.Slice(simdCount, remainder), + gLane.Slice(simdCount, remainder), + bLane.Slice(simdCount, remainder)); + } + } + + /// + public sealed override void ConvertToRgbInPlace(in ComponentValues values) + { + 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)Vector512.Count); + + int simdCount = length - remainder; + if (simdCount > 0) + { + this.ConvertToRgbInPlaceVectorized(values.Slice(0, simdCount)); + } + + if (remainder > 0) + { + this.ConvertToRgbInPlaceScalarRemainder(values.Slice(simdCount, remainder)); + } + } + + /// + /// Converts planar jpeg component values in + /// to RGB color space in place using API. + /// + /// The input/output as a stack-only struct + protected abstract void ConvertToRgbInPlaceVectorized(in ComponentValues values); + + /// + /// Converts remainder of the planar jpeg component values after + /// conversion in . + /// + /// The input/output as a stack-only struct + protected abstract void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values); + + /// + /// Converts RGB lanes to jpeg component values using API. + /// + /// Jpeg component values. + /// Red colors lane. + /// Green colors lane. + /// Blue colors lane. + protected abstract void ConvertFromRgbVectorized(in ComponentValues values, Span rLane, Span gLane, Span bLane); + + /// + /// Converts remainder of RGB lanes to jpeg component values after + /// conversion in . + /// + /// Jpeg component values. + /// Red colors lane. + /// Green colors lane. + /// Blue colors lane. + protected abstract void ConvertFromRgbScalarRemainder(in ComponentValues values, Span rLane, Span gLane, Span bLane); + } +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs index eb9224fee..897ef9375 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs @@ -17,7 +17,7 @@ public class YCbCrColorConversion : ColorConversionBenchmark [Benchmark] public void Scalar() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); new JpegColorConverterBase.YCbCrScalar(8).ConvertToRgbInPlace(values); } @@ -25,8 +25,32 @@ public class YCbCrColorConversion : ColorConversionBenchmark [Benchmark] public void SimdVector8() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); new JpegColorConverterBase.YCbCrVector(8).ConvertToRgbInPlace(values); } + + [Benchmark] + public void SimdVector128() + { + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); + + new JpegColorConverterBase.YCbCrVector128(8).ConvertToRgbInPlace(values); + } + + [Benchmark] + public void SimdVector256() + { + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); + + new JpegColorConverterBase.YCbCrVector256(8).ConvertToRgbInPlace(values); + } + + [Benchmark] + public void SimdVector512() + { + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); + + new JpegColorConverterBase.YCbCrVector512(8).ConvertToRgbInPlace(values); + } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 02a5d8907..69faa0bf4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; using SixLabors.ImageSharp.ColorProfiles; @@ -180,9 +181,17 @@ public class JpegColorConverterTests { // arrange Type expectedType = typeof(JpegColorConverterBase.YCbCrScalar); - if (Avx.IsSupported) + if (JpegColorConverterBase.JpegColorConverterVector512.IsSupported) { - expectedType = typeof(JpegColorConverterBase.YCbCrVector); + expectedType = typeof(JpegColorConverterBase.YCbCrVector512); + } + else if (JpegColorConverterBase.JpegColorConverterVector256.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.YCbCrVector256); + } + else if (JpegColorConverterBase.JpegColorConverterVector128.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.YCbCrVector128); } else if (Sse2.IsSupported) { From 597bc0c10158201fbfa1126a99865bdbf6267d05 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 May 2025 23:21:27 +1000 Subject: [PATCH 04/11] Update MultiplyAdd --- src/ImageSharp/Common/Helpers/Vector256Utilities.cs | 2 +- src/ImageSharp/Common/Helpers/Vector512Utilities.cs | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/Vector256Utilities.cs b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs index a7366f6da..9928b529e 100644 --- a/src/ImageSharp/Common/Helpers/Vector256Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs @@ -146,7 +146,7 @@ internal static class Vector256Utilities { if (Fma.IsSupported) { - return Fma.MultiplyAdd(vm1, vm0, va); + return Fma.MultiplyAdd(vm0, vm1, va); } if (Avx.IsSupported) diff --git a/src/ImageSharp/Common/Helpers/Vector512Utilities.cs b/src/ImageSharp/Common/Helpers/Vector512Utilities.cs index 77dc94475..40e8ac344 100644 --- a/src/ImageSharp/Common/Helpers/Vector512Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector512Utilities.cs @@ -145,7 +145,15 @@ internal static class Vector512Utilities public static Vector512 MultiplyAdd( Vector512 va, Vector512 vm0, - Vector512 vm1) => va + (vm0 * vm1); + Vector512 vm1) + { + if (Avx512F.IsSupported) + { + return Avx512F.FusedMultiplyAdd(vm0, vm1, va); + } + + return va + (vm0 * vm1); + } [DoesNotReturn] private static void ThrowUnreachableException() => throw new UnreachableException(); From 3b430e19c1fa4505949b82ab518cc98a8fcc31a5 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 5 May 2025 12:08:39 +1000 Subject: [PATCH 05/11] Port CMYK converter --- .../Common/Helpers/Vector128Utilities.cs | 10 - .../JpegColorConverter.CmykVector128.cs | 93 +++++++++ .../JpegColorConverter.CmykVector256.cs | 93 +++++++++ .../JpegColorConverter.CmykVector512.cs | 98 +++++++++ .../JpegColorConverter.YCbCrVector.cs | 126 ------------ .../ColorConverters/JpegColorConverterBase.cs | 19 +- .../ColorConversion/CmykColorConversion.cs | 24 ++- .../ColorConversion/YCbCrColorConversion.cs | 8 - .../Formats/Jpg/JpegColorConverterTests.cs | 187 ++++++++++++------ 9 files changed, 447 insertions(+), 211 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector128.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector256.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs diff --git a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs index 8f4ea6292..6b0581851 100644 --- a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs @@ -244,16 +244,6 @@ internal static class Vector128Utilities return Fma.MultiplyAdd(vm1, vm0, va); } - if (AdvSimd.IsSupported) - { - return AdvSimd.Add(AdvSimd.Multiply(vm0, vm1), va); - } - - if (Sse.IsSupported) - { - return Sse.Add(Sse.Multiply(vm0, vm1), va); - } - return va + (vm0 * vm1); } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector128.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector128.cs new file mode 100644 index 000000000..3b4c6d484 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector128.cs @@ -0,0 +1,93 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class CmykVector128 : JpegColorConverterVector128 + { + public CmykVector128(int precision) + : base(JpegColorSpace.Cmyk, precision) + { + } + + /// + public override void ConvertToRgbInPlace(in ComponentValues values) + { + ref Vector128 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 c1Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 c2Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector128 c3Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + + // Used for the color conversion + Vector128 scale = Vector128.Create(1 / (this.MaximumValue * this.MaximumValue)); + + nuint n = values.Component0.Vector128Count(); + for (nuint i = 0; i < n; i++) + { + ref Vector128 c = ref Unsafe.Add(ref c0Base, i); + ref Vector128 m = ref Unsafe.Add(ref c1Base, i); + ref Vector128 y = ref Unsafe.Add(ref c2Base, i); + Vector128 k = Unsafe.Add(ref c3Base, i); + + k *= scale; + c *= k; + m *= k; + y *= k; + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); + + public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span rLane, Span gLane, Span bLane) + { + ref Vector128 destC = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 destM = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 destY = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector128 destK = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + + ref Vector128 srcR = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector128 srcG = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector128 srcB = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + + Vector128 scale = Vector128.Create(maxValue); + + nuint n = values.Component0.Vector128Count(); + for (nuint i = 0; i < n; i++) + { + Vector128 ctmp = scale - Unsafe.Add(ref srcR, i); + Vector128 mtmp = scale - Unsafe.Add(ref srcG, i); + Vector128 ytmp = scale - Unsafe.Add(ref srcB, i); + Vector128 ktmp = Vector128.Min(ctmp, Vector128.Min(mtmp, ytmp)); + + Vector128 kMask = Vector128.Equals(ktmp, scale); + ctmp = Vector128.AndNot((ctmp - ktmp) / (scale - ktmp), kMask); + mtmp = Vector128.AndNot((mtmp - ktmp) / (scale - ktmp), kMask); + ytmp = Vector128.AndNot((ytmp - ktmp) / (scale - ktmp), kMask); + + 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; + } + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector256.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector256.cs new file mode 100644 index 000000000..77531c8fc --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector256.cs @@ -0,0 +1,93 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class CmykVector256 : JpegColorConverterVector256 + { + public CmykVector256(int precision) + : base(JpegColorSpace.Cmyk, precision) + { + } + + /// + public override void ConvertToRgbInPlace(in ComponentValues values) + { + 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 + Vector256 scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue)); + + nuint n = values.Component0.Vector256Count(); + for (nuint 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 *= scale; + c *= k; + m *= k; + y *= k; + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); + + public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span rLane, Span gLane, Span bLane) + { + ref Vector256 destC = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector256 destM = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector256 destY = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + 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)); + + Vector256 scale = Vector256.Create(maxValue); + + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) + { + Vector256 ctmp = scale - Unsafe.Add(ref srcR, i); + Vector256 mtmp = scale - Unsafe.Add(ref srcG, i); + Vector256 ytmp = scale - Unsafe.Add(ref srcB, i); + Vector256 ktmp = Vector256.Min(ctmp, Vector256.Min(mtmp, ytmp)); + + Vector256 kMask = Vector256.Equals(ktmp, scale); + ctmp = Vector256.AndNot((ctmp - ktmp) / (scale - ktmp), kMask); + mtmp = Vector256.AndNot((mtmp - ktmp) / (scale - ktmp), kMask); + ytmp = Vector256.AndNot((ytmp - ktmp) / (scale - ktmp), kMask); + + 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; + } + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs new file mode 100644 index 000000000..f5c479db2 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs @@ -0,0 +1,98 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class CmykVector512 : JpegColorConverterVector512 + { + public CmykVector512(int precision) + : base(JpegColorSpace.Cmyk, precision) + { + } + + /// + protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) + { + ref Vector512 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector512 c1Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector512 c2Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector512 c3Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + + // Used for the color conversion + Vector512 scale = Vector512.Create(1 / (this.MaximumValue * this.MaximumValue)); + + nuint n = values.Component0.Vector512Count(); + for (nuint i = 0; i < n; i++) + { + ref Vector512 c = ref Unsafe.Add(ref c0Base, i); + ref Vector512 m = ref Unsafe.Add(ref c1Base, i); + ref Vector512 y = ref Unsafe.Add(ref c2Base, i); + Vector512 k = Unsafe.Add(ref c3Base, i); + + k *= scale; + c *= k; + m *= k; + y *= k; + } + } + + /// + protected override void ConvertFromRgbVectorized(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + ref Vector512 destC = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector512 destM = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector512 destY = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector512 destK = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + + ref Vector512 srcR = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector512 srcG = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector512 srcB = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + + Vector512 scale = Vector512.Create(this.MaximumValue); + + nuint n = values.Component0.Vector512Count(); + for (nuint i = 0; i < n; i++) + { + Vector512 ctmp = scale - Unsafe.Add(ref srcR, i); + Vector512 mtmp = scale - Unsafe.Add(ref srcG, i); + Vector512 ytmp = scale - Unsafe.Add(ref srcB, i); + Vector512 ktmp = Vector512.Min(ctmp, Vector512.Min(mtmp, ytmp)); + + Vector512 kMask = Vector512.Equals(ktmp, scale); + ctmp = Vector512.AndNot((ctmp - ktmp) / (scale - ktmp), kMask); + mtmp = Vector512.AndNot((mtmp - ktmp) / (scale - ktmp), kMask); + ytmp = Vector512.AndNot((ytmp - ktmp) / (scale - ktmp), kMask); + + 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 ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) + => CmykScalar.ConvertToRgbInplace(values, this.MaximumValue); + + /// + protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => CmykScalar.ConvertFromRgb(values, this.MaximumValue, rLane, gLane, bLane); + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs deleted file mode 100644 index 92ff71b10..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// ReSharper disable ImpureMethodCallOnReadonlyValueField -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class YCbCrVector : JpegColorConverterVector - { - public YCbCrVector(int precision) - : base(JpegColorSpace.YCbCr, precision) - { - } - - /// - protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) - { - ref Vector c0Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector c1Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector c2Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - - Vector chromaOffset = new(-this.HalfValue); - - Vector scale = new(1 / this.MaximumValue); - Vector rCrMult = new(YCbCrScalar.RCrMult); - Vector gCbMult = new(-YCbCrScalar.GCbMult); - Vector gCrMult = new(-YCbCrScalar.GCrMult); - Vector bCbMult = new(YCbCrScalar.BCbMult); - - nuint n = values.Component0.VectorCount(); - for (nuint i = 0; i < n; i++) - { - // y = yVals[i]; - // cb = cbVals[i] - 128F; - // cr = crVals[i] - 128F; - 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 y = Unsafe.Add(ref c0Base, i); - Vector cb = Unsafe.Add(ref c1Base, i) + chromaOffset; - Vector cr = Unsafe.Add(ref c2Base, i) + chromaOffset; - - // r = y + (1.402F * cr); - // g = y - (0.344136F * cb) - (0.714136F * cr); - // b = y + (1.772F * cb); - Vector r = y + (cr * rCrMult); - Vector g = y + (cb * gCbMult) + (cr * gCrMult); - Vector b = y + (cb * bCbMult); - - r = r.RoundToNearestInteger(); - g = g.RoundToNearestInteger(); - b = b.RoundToNearestInteger(); - r *= scale; - g *= scale; - b *= scale; - - c0 = r; - c1 = g; - c2 = b; - } - } - - /// - protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) - => YCbCrScalar.ConvertToRgbInplace(values, this.MaximumValue, this.HalfValue); - - /// - protected override void ConvertFromRgbVectorized(in ComponentValues values, Span rLane, Span gLane, Span bLane) - { - ref Vector destY = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector destCb = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - 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)); - - Vector chromaOffset = new(this.HalfValue); - - Vector rYMult = new(0.299f); - Vector gYMult = new(0.587f); - Vector bYMult = new(0.114f); - - Vector rCbMult = new(0.168736f); - Vector gCbMult = new(0.331264f); - Vector bCbMult = new(0.5f); - - Vector rCrMult = new(0.5f); - Vector gCrMult = new(0.418688f); - Vector bCrMult = new(0.081312f); - - nuint n = values.Component0.VectorCount(); - for (nuint i = 0; i < n; i++) - { - 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) - 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 ConvertFromRgbScalarRemainder(in ComponentValues values, Span rLane, Span gLane, Span bLane) - => YCbCrScalar.ConvertFromRgb(values, this.HalfValue, rLane, gLane, bLane); - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs index 5681153a1..6ad62b682 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs @@ -131,11 +131,6 @@ internal abstract partial class JpegColorConverterBase return new YCbCrVector128(precision); } - if (JpegColorConverterVector.IsSupported) - { - return new YCbCrVector(precision); - } - return new YCbCrScalar(precision); } @@ -159,9 +154,19 @@ internal abstract partial class JpegColorConverterBase /// The precision in bits. private static JpegColorConverterBase GetCmykConverter(int precision) { - if (JpegColorConverterVector.IsSupported) + if (JpegColorConverterVector512.IsSupported) + { + return new CmykVector512(precision); + } + + if (JpegColorConverterVector256.IsSupported) + { + return new CmykVector256(precision); + } + + if (JpegColorConverterVector128.IsSupported) { - return new CmykVector(precision); + return new CmykVector128(precision); } return new CmykScalar(precision); diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs index e377a6f97..a1ba3fb8d 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs @@ -17,16 +17,32 @@ public class CmykColorConversion : ColorConversionBenchmark [Benchmark(Baseline = true)] public void Scalar() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); new JpegColorConverterBase.CmykScalar(8).ConvertToRgbInPlace(values); } [Benchmark] - public void SimdVector8() + public void SimdVector128() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); - new JpegColorConverterBase.CmykVector(8).ConvertToRgbInPlace(values); + new JpegColorConverterBase.CmykVector128(8).ConvertToRgbInPlace(values); + } + + [Benchmark] + public void SimdVector256() + { + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); + + new JpegColorConverterBase.CmykVector256(8).ConvertToRgbInPlace(values); + } + + [Benchmark] + public void SimdVector512() + { + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); + + new JpegColorConverterBase.CmykVector512(8).ConvertToRgbInPlace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs index 897ef9375..fbd762af4 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs @@ -22,14 +22,6 @@ public class YCbCrColorConversion : ColorConversionBenchmark new JpegColorConverterBase.YCbCrScalar(8).ConvertToRgbInPlace(values); } - [Benchmark] - public void SimdVector8() - { - JpegColorConverterBase.ComponentValues values = new(this.Input, 0); - - new JpegColorConverterBase.YCbCrVector(8).ConvertToRgbInPlace(values); - } - [Benchmark] public void SimdVector128() { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 69faa0bf4..41ab3b831 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; using SixLabors.ImageSharp.ColorProfiles; @@ -22,7 +21,7 @@ public class JpegColorConverterTests private const int TestBufferLength = 40; - private const HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2; + private const HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2; private static readonly ApproximateColorProfileComparer ColorSpaceComparer = new(epsilon: Precision); @@ -148,17 +147,17 @@ public class JpegColorConverterTests { // arrange Type expectedType = typeof(JpegColorConverterBase.CmykScalar); - if (Avx.IsSupported) + if (JpegColorConverterBase.JpegColorConverterVector512.IsSupported) { - expectedType = typeof(JpegColorConverterBase.CmykVector); + expectedType = typeof(JpegColorConverterBase.CmykVector512); } - else if (Sse2.IsSupported) + else if (JpegColorConverterBase.JpegColorConverterVector256.IsSupported) { - expectedType = typeof(JpegColorConverterBase.CmykVector); + expectedType = typeof(JpegColorConverterBase.CmykVector256); } - else if (AdvSimd.Arm64.IsSupported) + else if (JpegColorConverterBase.JpegColorConverterVector128.IsSupported) { - expectedType = typeof(JpegColorConverterBase.CmykVector); + expectedType = typeof(JpegColorConverterBase.CmykVector128); } // act @@ -175,7 +174,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -193,14 +192,6 @@ public class JpegColorConverterTests { expectedType = typeof(JpegColorConverterBase.YCbCrVector128); } - else if (Sse2.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.YCbCrVector); - } - else if (AdvSimd.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.YCbCrVector); - } // act JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.YCbCr, 8); @@ -216,7 +207,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -266,29 +257,60 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] - public void FromYCbCrVector(int seed) - { - JpegColorConverterBase.YCbCrVector converter = new(8); + public void FromYCbCrVector512(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.YCbCrVector512(8), + 3, + seed, + new JpegColorConverterBase.YCbCrScalar(8)); - if (!converter.IsAvailable) - { - this.Output.WriteLine( - $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); - return; - } + [Theory] + [MemberData(nameof(Seeds))] + public void FromYCbCrVector256(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.YCbCrVector256(8), + 3, + seed, + new JpegColorConverterBase.YCbCrScalar(8)); - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, + [Theory] + [MemberData(nameof(Seeds))] + public void FromYCbCrVector128(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.YCbCrVector128(8), + 3, seed, - IntrinsicsConfig); + new JpegColorConverterBase.YCbCrScalar(8)); - static void RunTest(string arg) => - ValidateConversionToRgb( - new JpegColorConverterBase.YCbCrVector(8), - 3, - FeatureTestRunner.Deserialize(arg), - new JpegColorConverterBase.YCbCrScalar(8)); - } + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToYCbCrVector512(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.YCbCrVector512(8), + 3, + seed, + new JpegColorConverterBase.YCbCrScalar(8), + precision: 2); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToYCbCrVector256(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.YCbCrVector256(8), + 3, + seed, + new JpegColorConverterBase.YCbCrScalar(8), + precision: 2); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToYCbCrVector128(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.YCbCrVector128(8), + 3, + seed, + new JpegColorConverterBase.YCbCrScalar(8), + precision: 2); [Theory] [MemberData(nameof(Seeds))] @@ -297,29 +319,60 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] - public void FromCmykVector(int seed) - { - JpegColorConverterBase.CmykVector converter = new(8); + public void FromCmykVector512(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.CmykVector512(8), + 4, + seed, + new JpegColorConverterBase.CmykScalar(8)); - if (!converter.IsAvailable) - { - this.Output.WriteLine( - $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); - return; - } + [Theory] + [MemberData(nameof(Seeds))] + public void FromCmykVector256(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.CmykVector256(8), + 4, + seed, + new JpegColorConverterBase.CmykScalar(8)); - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, + [Theory] + [MemberData(nameof(Seeds))] + public void FromCmykVector128(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.CmykVector128(8), + 4, seed, - IntrinsicsConfig); + new JpegColorConverterBase.CmykScalar(8)); - static void RunTest(string arg) => - ValidateConversionToRgb( - new JpegColorConverterBase.CmykVector(8), - 4, - FeatureTestRunner.Deserialize(arg), - new JpegColorConverterBase.CmykScalar(8)); - } + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToCmykVector512(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.CmykVector512(8), + 4, + seed, + new JpegColorConverterBase.CmykScalar(8), + precision: 2); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToCmykVector256(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.CmykVector256(8), + 4, + seed, + new JpegColorConverterBase.CmykScalar(8), + precision: 2); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToCmykVector128(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.CmykVector128(8), + 4, + seed, + new JpegColorConverterBase.CmykScalar(8), + precision: 2); [Theory] [MemberData(nameof(Seeds))] @@ -434,6 +487,28 @@ public class JpegColorConverterTests baseLineConverter); } + private void TestConversionFromRgb( + JpegColorConverterBase converter, + int componentCount, + int seed, + JpegColorConverterBase baseLineConverter, + int precision) + { + if (!converter.IsAvailable) + { + this.Output.WriteLine( + $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); + return; + } + + ValidateConversionFromRgb( + converter, + componentCount, + seed, + baseLineConverter, + precision); + } + private static JpegColorConverterBase.ComponentValues CreateRandomValues( int length, int componentCount, From 87438a3a11ddf18909de942449bd91c60451ef07 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 5 May 2025 13:17:22 +1000 Subject: [PATCH 06/11] Port Grayscale converter --- .../JpegColorConverter.CmykScalar.cs | 12 +-- .../JpegColorConverter.GrayScaleScalar.cs | 19 ++-- .../JpegColorConverter.GrayScaleVector.cs | 4 +- .../JpegColorConverter.GrayScaleVector128.cs | 67 ++++++++++++++ .../JpegColorConverter.GrayScaleVector256.cs | 67 ++++++++++++++ .../JpegColorConverter.GrayScaleVector512.cs | 75 ++++++++++++++++ .../JpegColorConverter.RgbScalar.cs | 22 ++--- .../JpegColorConverter.RgbVector.cs | 2 +- .../JpegColorConverter.YCbCrScalar.cs | 8 +- .../JpegColorConverter.YCbCrVector512.cs | 2 +- .../ColorConverters/JpegColorConverterBase.cs | 16 +++- .../GrayscaleColorConversion.cs | 30 +++++-- .../Formats/Jpg/JpegColorConverterTests.cs | 89 +++++++++++++------ 13 files changed, 339 insertions(+), 74 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector128.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector256.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector512.cs diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs index 1b3be69fa..60078db38 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs @@ -17,8 +17,8 @@ internal abstract partial class JpegColorConverterBase ConvertToRgbInplace(values, this.MaximumValue); /// - public override void ConvertFromRgb(in ComponentValues values, Span r, Span g, Span b) - => ConvertFromRgb(values, this.MaximumValue, r, g, b); + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => ConvertFromRgb(values, this.MaximumValue, rLane, gLane, bLane); public static void ConvertToRgbInplace(in ComponentValues values, float maxValue) { @@ -42,7 +42,7 @@ internal abstract partial class JpegColorConverterBase } } - public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span r, Span g, Span b) + public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span rLane, Span gLane, Span bLane) { Span c = values.Component0; Span m = values.Component1; @@ -51,9 +51,9 @@ internal abstract partial class JpegColorConverterBase for (int i = 0; i < c.Length; i++) { - float ctmp = 255f - r[i]; - float mtmp = 255f - g[i]; - float ytmp = 255f - b[i]; + float ctmp = 255f - rLane[i]; + float mtmp = 255f - gLane[i]; + float ytmp = 255f - bLane[i]; float ktmp = MathF.Min(MathF.Min(ctmp, mtmp), ytmp); if (ktmp >= 255f) diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs index c01811182..f710b7365 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs @@ -8,22 +8,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components; internal abstract partial class JpegColorConverterBase { - internal sealed class GrayscaleScalar : JpegColorConverterScalar + internal sealed class GrayScaleScalar : JpegColorConverterScalar { - public GrayscaleScalar(int precision) + public GrayScaleScalar(int precision) : base(JpegColorSpace.Grayscale, precision) { } /// public override void ConvertToRgbInPlace(in ComponentValues values) - => ConvertToRgbInplace(values.Component0, this.MaximumValue); + => ConvertToRgbInPlace(values.Component0, this.MaximumValue); /// - public override void ConvertFromRgb(in ComponentValues values, Span r, Span g, Span b) - => ConvertCoreInplaceFromRgb(values, r, g, b); + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => ConvertFromRgbScalar(values, rLane, gLane, bLane); - internal static void ConvertToRgbInplace(Span values, float maxValue) + internal static void ConvertToRgbInPlace(Span values, float maxValue) { ref float valuesRef = ref MemoryMarshal.GetReference(values); float scale = 1 / maxValue; @@ -34,15 +34,14 @@ internal abstract partial class JpegColorConverterBase } } - internal static void ConvertCoreInplaceFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + internal static void ConvertFromRgbScalar(in ComponentValues values, Span rLane, Span gLane, Span bLane) { Span c0 = values.Component0; for (int i = 0; i < c0.Length; i++) { - // luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b) - float luma = (0.299f * rLane[i]) + (0.587f * gLane[i]) + (0.114f * bLane[i]); - c0[i] = luma; + // luminosity = (0.299 * r) + (0.587 * g) + (0.114 * b) + c0[i] = (float)((0.299f * rLane[i]) + (0.587f * gLane[i]) + (0.114f * bLane[i])); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs index b6283084f..c9e1a3499 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs @@ -34,7 +34,7 @@ internal abstract partial class JpegColorConverterBase /// protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) - => GrayscaleScalar.ConvertToRgbInplace(values.Component0, this.MaximumValue); + => GrayScaleScalar.ConvertToRgbInPlace(values.Component0, this.MaximumValue); /// protected override void ConvertFromRgbVectorized(in ComponentValues values, Span rLane, Span gLane, Span bLane) @@ -67,6 +67,6 @@ internal abstract partial class JpegColorConverterBase /// protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span r, Span g, Span b) - => GrayscaleScalar.ConvertCoreInplaceFromRgb(values, r, g, b); + => GrayScaleScalar.ConvertFromRgbScalar(values, r, g, b); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector128.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector128.cs new file mode 100644 index 000000000..f3a6f7d37 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector128.cs @@ -0,0 +1,67 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.Common.Helpers; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class GrayScaleVector128 : JpegColorConverterVector128 + { + public GrayScaleVector128(int precision) + : base(JpegColorSpace.Grayscale, precision) + { + } + + /// + public override void ConvertToRgbInPlace(in ComponentValues values) + { + ref Vector128 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + + // Used for the color conversion + Vector128 scale = Vector128.Create(1 / this.MaximumValue); + + nuint n = values.Component0.Vector128Count(); + for (nuint i = 0; i < n; i++) + { + ref Vector128 c0 = ref Unsafe.Add(ref c0Base, i); + c0 *= scale; + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + ref Vector128 destLuminance = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + + ref Vector128 srcRed = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector128 srcGreen = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector128 srcBlue = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + + // Used for the color conversion + Vector128 f0299 = Vector128.Create(0.299f); + Vector128 f0587 = Vector128.Create(0.587f); + Vector128 f0114 = Vector128.Create(0.114f); + + nuint n = values.Component0.Vector128Count(); + for (nuint i = 0; i < n; i++) + { + ref Vector128 r = ref Unsafe.Add(ref srcRed, i); + ref Vector128 g = ref Unsafe.Add(ref srcGreen, i); + ref Vector128 b = ref Unsafe.Add(ref srcBlue, i); + + // luminosity = (0.299 * r) + (0.587 * g) + (0.114 * b) + Unsafe.Add(ref destLuminance, i) = Vector128Utilities.MultiplyAdd(Vector128Utilities.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + } + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector256.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector256.cs new file mode 100644 index 000000000..12ced3dcd --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector256.cs @@ -0,0 +1,67 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.Common.Helpers; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class GrayScaleVector256 : JpegColorConverterVector256 + { + public GrayScaleVector256(int precision) + : base(JpegColorSpace.Grayscale, precision) + { + } + + /// + public override void ConvertToRgbInPlace(in ComponentValues values) + { + ref Vector256 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + + // Used for the color conversion + Vector256 scale = Vector256.Create(1 / this.MaximumValue); + + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) + { + ref Vector256 c0 = ref Unsafe.Add(ref c0Base, i); + c0 *= scale; + } + } + + /// + public override void ConvertFromRgb(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 + Vector256 f0299 = Vector256.Create(0.299f); + Vector256 f0587 = Vector256.Create(0.587f); + Vector256 f0114 = Vector256.Create(0.114f); + + nuint n = values.Component0.Vector256Count(); + for (nuint 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); + + // luminosity = (0.299 * r) + (0.587 * g) + (0.114 * b) + Unsafe.Add(ref destLuminance, i) = Vector256Utilities.MultiplyAdd(Vector256Utilities.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + } + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector512.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector512.cs new file mode 100644 index 000000000..163cb6656 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector512.cs @@ -0,0 +1,75 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.Common.Helpers; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class GrayScaleVector512 : JpegColorConverterVector512 + { + public GrayScaleVector512(int precision) + : base(JpegColorSpace.Grayscale, precision) + { + } + + /// + protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) + { + ref Vector512 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + + // Used for the color conversion + Vector512 scale = Vector512.Create(1 / this.MaximumValue); + + nuint n = values.Component0.Vector512Count(); + for (nuint i = 0; i < n; i++) + { + ref Vector512 c0 = ref Unsafe.Add(ref c0Base, i); + c0 *= scale; + } + } + + /// + protected override void ConvertFromRgbVectorized(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + ref Vector512 destLuminance = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + + ref Vector512 srcRed = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector512 srcGreen = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector512 srcBlue = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + + // Used for the color conversion + Vector512 f0299 = Vector512.Create(0.299f); + Vector512 f0587 = Vector512.Create(0.587f); + Vector512 f0114 = Vector512.Create(0.114f); + + nuint n = values.Component0.Vector512Count(); + for (nuint i = 0; i < n; i++) + { + ref Vector512 r = ref Unsafe.Add(ref srcRed, i); + ref Vector512 g = ref Unsafe.Add(ref srcGreen, i); + ref Vector512 b = ref Unsafe.Add(ref srcBlue, i); + + // luminosity = (0.299 * r) + (0.587 * g) + (0.114 * b) + Unsafe.Add(ref destLuminance, i) = Vector512Utilities.MultiplyAdd(Vector512Utilities.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + } + } + + /// + protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) + => GrayScaleScalar.ConvertToRgbInPlace(values.Component0, this.MaximumValue); + + /// + protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span r, Span g, Span b) + => GrayScaleScalar.ConvertFromRgbScalar(values, r, g, b); + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs index f62b2c7ff..23825b06e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs @@ -14,24 +14,24 @@ internal abstract partial class JpegColorConverterBase /// public override void ConvertToRgbInPlace(in ComponentValues values) - => ConvertToRgbInplace(values, this.MaximumValue); + => ConvertToRgbInPlace(values, this.MaximumValue); /// - public override void ConvertFromRgb(in ComponentValues values, Span r, Span g, Span b) - => ConvertFromRgb(values, r, g, b); + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => ConvertFromRgb(values, rLane, gLane, bLane); - internal static void ConvertToRgbInplace(ComponentValues values, float maxValue) + internal static void ConvertToRgbInPlace(ComponentValues values, float maxValue) { - GrayscaleScalar.ConvertToRgbInplace(values.Component0, maxValue); - GrayscaleScalar.ConvertToRgbInplace(values.Component1, maxValue); - GrayscaleScalar.ConvertToRgbInplace(values.Component2, maxValue); + GrayScaleScalar.ConvertToRgbInPlace(values.Component0, maxValue); + GrayScaleScalar.ConvertToRgbInPlace(values.Component1, maxValue); + GrayScaleScalar.ConvertToRgbInPlace(values.Component2, maxValue); } - internal static void ConvertFromRgb(ComponentValues values, Span r, Span g, Span b) + internal static void ConvertFromRgb(ComponentValues values, Span rLane, Span gLane, Span bLane) { - r.CopyTo(values.Component0); - g.CopyTo(values.Component1); - b.CopyTo(values.Component2); + rLane.CopyTo(values.Component0); + gLane.CopyTo(values.Component1); + bLane.CopyTo(values.Component2); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs index 5fabc80c7..2b4ae2af1 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs @@ -42,7 +42,7 @@ internal abstract partial class JpegColorConverterBase /// protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) - => RgbScalar.ConvertToRgbInplace(values, this.MaximumValue); + => RgbScalar.ConvertToRgbInPlace(values, this.MaximumValue); /// protected override void ConvertFromRgbVectorized(in ComponentValues values, Span r, Span g, Span b) diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs index e7e29a87a..e514a0166 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs @@ -20,13 +20,13 @@ internal abstract partial class JpegColorConverterBase /// public override void ConvertToRgbInPlace(in ComponentValues values) - => ConvertToRgbInplace(values, this.MaximumValue, this.HalfValue); + => ConvertToRgbInPlace(values, this.MaximumValue, this.HalfValue); /// - public override void ConvertFromRgb(in ComponentValues values, Span r, Span g, Span b) - => ConvertFromRgb(values, this.HalfValue, r, g, b); + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => ConvertFromRgb(values, this.HalfValue, rLane, gLane, bLane); - public static void ConvertToRgbInplace(in ComponentValues values, float maxValue, float halfValue) + public static void ConvertToRgbInPlace(in ComponentValues values, float maxValue, float halfValue) { Span c0 = values.Component0; Span c1 = values.Component1; diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector512.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector512.cs index 79ad03512..def65b4a7 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector512.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector512.cs @@ -114,7 +114,7 @@ internal abstract partial class JpegColorConverterBase /// protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) - => YCbCrScalar.ConvertToRgbInplace(values, this.MaximumValue, this.HalfValue); + => YCbCrScalar.ConvertToRgbInPlace(values, this.MaximumValue, this.HalfValue); /// protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span rLane, Span gLane, Span bLane) diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs index 6ad62b682..96eb927d0 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs @@ -178,12 +178,22 @@ internal abstract partial class JpegColorConverterBase /// The precision in bits. private static JpegColorConverterBase GetGrayScaleConverter(int precision) { - if (JpegColorConverterVector.IsSupported) + if (JpegColorConverterVector512.IsSupported) + { + return new GrayScaleVector512(precision); + } + + if (JpegColorConverterVector256.IsSupported) + { + return new GrayScaleVector256(precision); + } + + if (JpegColorConverterVector128.IsSupported) { - return new GrayScaleVector(precision); + return new GrayScaleVector128(precision); } - return new GrayscaleScalar(precision); + return new GrayScaleScalar(precision); } /// diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs index a63a97152..3ade4279f 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs @@ -7,9 +7,9 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; [Config(typeof(Config.Short))] -public class GrayscaleColorConversion : ColorConversionBenchmark +public class GrayScaleColorConversion : ColorConversionBenchmark { - public GrayscaleColorConversion() + public GrayScaleColorConversion() : base(1) { } @@ -17,16 +17,32 @@ public class GrayscaleColorConversion : ColorConversionBenchmark [Benchmark(Baseline = true)] public void Scalar() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); - new JpegColorConverterBase.GrayscaleScalar(8).ConvertToRgbInPlace(values); + new JpegColorConverterBase.GrayScaleScalar(8).ConvertToRgbInPlace(values); } [Benchmark] - public void SimdVector8() + public void SimdVector128() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); - new JpegColorConverterBase.GrayScaleVector(8).ConvertToRgbInPlace(values); + new JpegColorConverterBase.GrayScaleVector128(8).ConvertToRgbInPlace(values); + } + + [Benchmark] + public void SimdVector256() + { + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); + + new JpegColorConverterBase.GrayScaleVector256(8).ConvertToRgbInPlace(values); + } + + [Benchmark] + public void SimdVector512() + { + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); + + new JpegColorConverterBase.GrayScaleVector512(8).ConvertToRgbInPlace(values); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 41ab3b831..52b5d3183 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -108,23 +108,23 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { // arrange - Type expectedType = typeof(JpegColorConverterBase.GrayscaleScalar); - if (Avx.IsSupported) + Type expectedType = typeof(JpegColorConverterBase.GrayScaleScalar); + if (JpegColorConverterBase.JpegColorConverterVector512.IsSupported) { - expectedType = typeof(JpegColorConverterBase.GrayScaleVector); + expectedType = typeof(JpegColorConverterBase.GrayScaleVector512); } - else if (Sse2.IsSupported) + else if (JpegColorConverterBase.JpegColorConverterVector256.IsSupported) { - expectedType = typeof(JpegColorConverterBase.GrayScaleVector); + expectedType = typeof(JpegColorConverterBase.GrayScaleVector256); } - else if (AdvSimd.IsSupported) + else if (JpegColorConverterBase.JpegColorConverterVector128.IsSupported) { - expectedType = typeof(JpegColorConverterBase.GrayScaleVector); + expectedType = typeof(JpegColorConverterBase.GrayScaleVector128); } // act @@ -376,34 +376,65 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] - public void FromGrayscaleBasic(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.GrayscaleScalar(8), 1, seed); + public void FromGrayScaleBasic(int seed) => + this.TestConversionToRgb(new JpegColorConverterBase.GrayScaleScalar(8), 1, seed); [Theory] [MemberData(nameof(Seeds))] - public void FromGrayscaleVector(int seed) - { - JpegColorConverterBase.GrayScaleVector converter = new(8); + public void FromGrayScaleVector512(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.GrayScaleVector512(8), + 1, + seed, + new JpegColorConverterBase.GrayScaleScalar(8)); - if (!converter.IsAvailable) - { - this.Output.WriteLine( - $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); - return; - } + [Theory] + [MemberData(nameof(Seeds))] + public void FromGrayScaleVector256(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.GrayScaleVector256(8), + 1, + seed, + new JpegColorConverterBase.GrayScaleScalar(8)); - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, + [Theory] + [MemberData(nameof(Seeds))] + public void FromGrayScaleVector128(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.GrayScaleVector128(8), + 1, seed, - IntrinsicsConfig); + new JpegColorConverterBase.GrayScaleScalar(8)); - static void RunTest(string arg) => - ValidateConversionToRgb( - new JpegColorConverterBase.GrayScaleVector(8), - 1, - FeatureTestRunner.Deserialize(arg), - new JpegColorConverterBase.GrayscaleScalar(8)); - } + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToGrayScaleVector512(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.GrayScaleVector512(8), + 1, + seed, + new JpegColorConverterBase.GrayScaleScalar(8), + precision: 2); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToGrayScaleVector256(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.GrayScaleVector256(8), + 1, + seed, + new JpegColorConverterBase.GrayScaleScalar(8), + precision: 2); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToGrayScaleVector128(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.GrayScaleVector128(8), + 1, + seed, + new JpegColorConverterBase.GrayScaleScalar(8), + precision: 2); [Theory] [MemberData(nameof(Seeds))] From 80fee7fa121072911bbf8bf52034a307362a2e45 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 5 May 2025 14:13:58 +1000 Subject: [PATCH 07/11] Port YccK and use alias --- .../JpegColorConverter.CmykVector512.cs | 21 +-- .../JpegColorConverter.GrayScaleVector256.cs | 4 +- .../JpegColorConverter.GrayScaleVector512.cs | 8 +- .../JpegColorConverter.YCbCrVector128.cs | 20 +-- .../JpegColorConverter.YCbCrVector256.cs | 20 +-- .../JpegColorConverter.YCbCrVector512.cs | 20 +-- .../JpegColorConverter.YccKScalar.cs | 8 +- .../JpegColorConverter.YccKVector.cs | 2 +- .../JpegColorConverter.YccKVector128.cs | 130 ++++++++++++++++ .../JpegColorConverter.YccKVector256.cs | 130 ++++++++++++++++ .../JpegColorConverter.YccKVector512.cs | 144 ++++++++++++++++++ .../ColorConverters/JpegColorConverterBase.cs | 14 +- .../ColorConversion/YccKColorConverter.cs | 28 +++- .../Formats/Jpg/JpegColorConverterTests.cs | 81 +++++++--- 14 files changed, 551 insertions(+), 79 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector128.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector256.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector512.cs diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs index f5c479db2..6daf4f8b8 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs @@ -48,6 +48,17 @@ internal abstract partial class JpegColorConverterBase /// protected override void ConvertFromRgbVectorized(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => ConvertFromRgbVectorized(in values, this.MaximumValue, rLane, gLane, bLane); + + /// + protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) + => CmykScalar.ConvertToRgbInplace(values, this.MaximumValue); + + /// + protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => CmykScalar.ConvertFromRgb(values, this.MaximumValue, rLane, gLane, bLane); + + internal static void ConvertFromRgbVectorized(in ComponentValues values, float maxValue, Span rLane, Span gLane, Span bLane) { ref Vector512 destC = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); @@ -65,7 +76,7 @@ internal abstract partial class JpegColorConverterBase ref Vector512 srcB = ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); - Vector512 scale = Vector512.Create(this.MaximumValue); + Vector512 scale = Vector512.Create(maxValue); nuint n = values.Component0.Vector512Count(); for (nuint i = 0; i < n; i++) @@ -86,13 +97,5 @@ internal abstract partial class JpegColorConverterBase Unsafe.Add(ref destK, i) = scale - ktmp; } } - - /// - protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) - => CmykScalar.ConvertToRgbInplace(values, this.MaximumValue); - - /// - protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span rLane, Span gLane, Span bLane) - => CmykScalar.ConvertFromRgb(values, this.MaximumValue, rLane, gLane, bLane); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector256.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector256.cs index 12ced3dcd..139ffc549 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector256.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector256.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; -using SixLabors.ImageSharp.Common.Helpers; +using Vector256_ = SixLabors.ImageSharp.Common.Helpers.Vector256Utilities; namespace SixLabors.ImageSharp.Formats.Jpeg.Components; @@ -60,7 +60,7 @@ internal abstract partial class JpegColorConverterBase ref Vector256 b = ref Unsafe.Add(ref srcBlue, i); // luminosity = (0.299 * r) + (0.587 * g) + (0.114 * b) - Unsafe.Add(ref destLuminance, i) = Vector256Utilities.MultiplyAdd(Vector256Utilities.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + Unsafe.Add(ref destLuminance, i) = Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(f0114 * b, f0587, g), f0299, r); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector512.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector512.cs index 163cb6656..21d5eaa6f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector512.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector512.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; -using SixLabors.ImageSharp.Common.Helpers; +using Vector512_ = SixLabors.ImageSharp.Common.Helpers.Vector512Utilities; namespace SixLabors.ImageSharp.Formats.Jpeg.Components; @@ -60,7 +60,7 @@ internal abstract partial class JpegColorConverterBase ref Vector512 b = ref Unsafe.Add(ref srcBlue, i); // luminosity = (0.299 * r) + (0.587 * g) + (0.114 * b) - Unsafe.Add(ref destLuminance, i) = Vector512Utilities.MultiplyAdd(Vector512Utilities.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + Unsafe.Add(ref destLuminance, i) = Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(f0114 * b, f0587, g), f0299, r); } } @@ -69,7 +69,7 @@ internal abstract partial class JpegColorConverterBase => GrayScaleScalar.ConvertToRgbInPlace(values.Component0, this.MaximumValue); /// - protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span r, Span g, Span b) - => GrayScaleScalar.ConvertFromRgbScalar(values, r, g, b); + protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => GrayScaleScalar.ConvertFromRgbScalar(values, rLane, gLane, bLane); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector128.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector128.cs index 95e3167cc..8cecd3956 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector128.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector128.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; -using SixLabors.ImageSharp.Common.Helpers; +using Vector128_ = SixLabors.ImageSharp.Common.Helpers.Vector128Utilities; namespace SixLabors.ImageSharp.Formats.Jpeg.Components; @@ -52,13 +52,13 @@ internal abstract partial class JpegColorConverterBase // r = y + (1.402F * cr); // g = y - (0.344136F * cb) - (0.714136F * cr); // b = y + (1.772F * cb); - Vector128 r = Vector128Utilities.MultiplyAdd(y, cr, rCrMult); - Vector128 g = Vector128Utilities.MultiplyAdd(Vector128Utilities.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); - Vector128 b = Vector128Utilities.MultiplyAdd(y, cb, bCbMult); + Vector128 r = Vector128_.MultiplyAdd(y, cr, rCrMult); + Vector128 g = Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); + Vector128 b = Vector128_.MultiplyAdd(y, cb, bCbMult); - r = Vector128Utilities.RoundToNearestInteger(r) * scale; - g = Vector128Utilities.RoundToNearestInteger(g) * scale; - b = Vector128Utilities.RoundToNearestInteger(b) * scale; + r = Vector128_.RoundToNearestInteger(r) * scale; + g = Vector128_.RoundToNearestInteger(g) * scale; + b = Vector128_.RoundToNearestInteger(b) * scale; c0 = r; c1 = g; @@ -103,9 +103,9 @@ internal abstract partial class JpegColorConverterBase // 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) - Vector128 y = Vector128Utilities.MultiplyAdd(Vector128Utilities.MultiplyAdd(f0114 * b, f0587, g), f0299, r); - Vector128 cb = chromaOffset + Vector128Utilities.MultiplyAdd(Vector128Utilities.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r); - Vector128 cr = chromaOffset + Vector128Utilities.MultiplyAdd(Vector128Utilities.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r); + Vector128 y = Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + Vector128 cb = chromaOffset + Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r); + Vector128 cr = chromaOffset + Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r); Unsafe.Add(ref destY, i) = y; Unsafe.Add(ref destCb, i) = cb; diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector256.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector256.cs index 8d8e23468..f8517e086 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector256.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector256.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; -using SixLabors.ImageSharp.Common.Helpers; +using Vector256_ = SixLabors.ImageSharp.Common.Helpers.Vector256Utilities; namespace SixLabors.ImageSharp.Formats.Jpeg.Components; @@ -52,13 +52,13 @@ internal abstract partial class JpegColorConverterBase // r = y + (1.402F * cr); // g = y - (0.344136F * cb) - (0.714136F * cr); // b = y + (1.772F * cb); - Vector256 r = Vector256Utilities.MultiplyAdd(y, cr, rCrMult); - Vector256 g = Vector256Utilities.MultiplyAdd(Vector256Utilities.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); - Vector256 b = Vector256Utilities.MultiplyAdd(y, cb, bCbMult); + Vector256 r = Vector256_.MultiplyAdd(y, cr, rCrMult); + Vector256 g = Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); + Vector256 b = Vector256_.MultiplyAdd(y, cb, bCbMult); - r = Vector256Utilities.RoundToNearestInteger(r) * scale; - g = Vector256Utilities.RoundToNearestInteger(g) * scale; - b = Vector256Utilities.RoundToNearestInteger(b) * scale; + r = Vector256_.RoundToNearestInteger(r) * scale; + g = Vector256_.RoundToNearestInteger(g) * scale; + b = Vector256_.RoundToNearestInteger(b) * scale; c0 = r; c1 = g; @@ -103,9 +103,9 @@ internal abstract partial class JpegColorConverterBase // 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) - Vector256 y = Vector256Utilities.MultiplyAdd(Vector256Utilities.MultiplyAdd(f0114 * b, f0587, g), f0299, r); - Vector256 cb = chromaOffset + Vector256Utilities.MultiplyAdd(Vector256Utilities.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r); - Vector256 cr = chromaOffset + Vector256Utilities.MultiplyAdd(Vector256Utilities.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r); + Vector256 y = Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + Vector256 cb = chromaOffset + Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r); + Vector256 cr = chromaOffset + Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r); Unsafe.Add(ref destY, i) = y; Unsafe.Add(ref destCb, i) = cb; diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector512.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector512.cs index def65b4a7..7598a64b2 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector512.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector512.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; -using SixLabors.ImageSharp.Common.Helpers; +using Vector512_ = SixLabors.ImageSharp.Common.Helpers.Vector512Utilities; namespace SixLabors.ImageSharp.Formats.Jpeg.Components; @@ -51,13 +51,13 @@ internal abstract partial class JpegColorConverterBase // r = y + (1.402F * cr); // g = y - (0.344136F * cb) - (0.714136F * cr); // b = y + (1.772F * cb); - Vector512 r = Vector512Utilities.MultiplyAdd(y, cr, rCrMult); - Vector512 g = Vector512Utilities.MultiplyAdd(Vector512Utilities.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); - Vector512 b = Vector512Utilities.MultiplyAdd(y, cb, bCbMult); + Vector512 r = Vector512_.MultiplyAdd(y, cr, rCrMult); + Vector512 g = Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); + Vector512 b = Vector512_.MultiplyAdd(y, cb, bCbMult); - r = Vector512Utilities.RoundToNearestInteger(r) * scale; - g = Vector512Utilities.RoundToNearestInteger(g) * scale; - b = Vector512Utilities.RoundToNearestInteger(b) * scale; + r = Vector512_.RoundToNearestInteger(r) * scale; + g = Vector512_.RoundToNearestInteger(g) * scale; + b = Vector512_.RoundToNearestInteger(b) * scale; c0 = r; c1 = g; @@ -102,9 +102,9 @@ internal abstract partial class JpegColorConverterBase // 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) - Vector512 y = Vector512Utilities.MultiplyAdd(Vector512Utilities.MultiplyAdd(f0114 * b, f0587, g), f0299, r); - Vector512 cb = chromaOffset + Vector512Utilities.MultiplyAdd(Vector512Utilities.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r); - Vector512 cr = chromaOffset + Vector512Utilities.MultiplyAdd(Vector512Utilities.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r); + Vector512 y = Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + Vector512 cb = chromaOffset + Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r); + Vector512 cr = chromaOffset + Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r); Unsafe.Add(ref destY, i) = y; Unsafe.Add(ref destCb, i) = cb; diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs index 610c0a84e..bb545ec76 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs @@ -20,13 +20,13 @@ internal abstract partial class JpegColorConverterBase /// public override void ConvertToRgbInPlace(in ComponentValues values) - => ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue); + => ConvertToRgpInPlace(values, this.MaximumValue, this.HalfValue); /// - public override void ConvertFromRgb(in ComponentValues values, Span r, Span g, Span b) - => ConvertFromRgb(values, this.HalfValue, this.MaximumValue, r, g, b); + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => ConvertFromRgb(values, this.HalfValue, this.MaximumValue, rLane, gLane, bLane); - public static void ConvertToRgpInplace(in ComponentValues values, float maxValue, float halfValue) + public static void ConvertToRgpInPlace(in ComponentValues values, float maxValue, float halfValue) { Span c0 = values.Component0; Span c1 = values.Component1; diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs index 5d57d0e54..af144240e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs @@ -71,7 +71,7 @@ internal abstract partial class JpegColorConverterBase /// protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) - => YccKScalar.ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue); + => YccKScalar.ConvertToRgpInPlace(values, this.MaximumValue, this.HalfValue); /// protected override void ConvertFromRgbVectorized(in ComponentValues values, Span rLane, Span gLane, Span bLane) diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector128.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector128.cs new file mode 100644 index 000000000..5bb2c5e5b --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector128.cs @@ -0,0 +1,130 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using Vector128_ = SixLabors.ImageSharp.Common.Helpers.Vector128Utilities; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class YccKVector128 : JpegColorConverterVector128 + { + public YccKVector128(int precision) + : base(JpegColorSpace.Ycck, precision) + { + } + + /// + public override void ConvertToRgbInPlace(in ComponentValues values) + { + ref Vector128 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 c1Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 c2Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector128 kBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + + // Used for the color conversion + Vector128 chromaOffset = Vector128.Create(-this.HalfValue); + Vector128 scale = Vector128.Create(1 / (this.MaximumValue * this.MaximumValue)); + Vector128 max = Vector128.Create(this.MaximumValue); + Vector128 rCrMult = Vector128.Create(YCbCrScalar.RCrMult); + Vector128 gCbMult = Vector128.Create(-YCbCrScalar.GCbMult); + Vector128 gCrMult = Vector128.Create(-YCbCrScalar.GCrMult); + Vector128 bCbMult = Vector128.Create(YCbCrScalar.BCbMult); + + // Walking 8 elements at one step: + nuint n = values.Component0.Vector128Count(); + for (nuint i = 0; i < n; i++) + { + // y = yVals[i]; + // cb = cbVals[i] - 128F; + // cr = crVals[i] - 128F; + // k = kVals[i] / 256F; + ref Vector128 c0 = ref Unsafe.Add(ref c0Base, i); + ref Vector128 c1 = ref Unsafe.Add(ref c1Base, i); + ref Vector128 c2 = ref Unsafe.Add(ref c2Base, i); + Vector128 y = c0; + Vector128 cb = c1 + chromaOffset; + Vector128 cr = c2 + chromaOffset; + Vector128 scaledK = Unsafe.Add(ref kBase, i) * scale; + + // r = y + (1.402F * cr); + // g = y - (0.344136F * cb) - (0.714136F * cr); + // b = y + (1.772F * cb); + Vector128 r = Vector128_.MultiplyAdd(y, cr, rCrMult); + Vector128 g = Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); + Vector128 b = Vector128_.MultiplyAdd(y, cb, bCbMult); + + r = max - Vector128_.RoundToNearestInteger(r); + g = max - Vector128_.RoundToNearestInteger(g); + b = max - Vector128_.RoundToNearestInteger(b); + + r *= scaledK; + g *= scaledK; + b *= scaledK; + + c0 = r; + c1 = g; + c2 = b; + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + // rgb -> cmyk + CmykVector128.ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); + + // cmyk -> ycck + ref Vector128 destY = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 destCb = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 destCr = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + ref Vector128 srcR = ref destY; + ref Vector128 srcG = ref destCb; + ref Vector128 srcB = ref destCr; + + // Used for the color conversion + Vector128 maxSampleValue = Vector128.Create(this.MaximumValue); + + Vector128 chromaOffset = Vector128.Create(this.HalfValue); + + Vector128 f0299 = Vector128.Create(0.299f); + Vector128 f0587 = Vector128.Create(0.587f); + Vector128 f0114 = Vector128.Create(0.114f); + Vector128 fn0168736 = Vector128.Create(-0.168736f); + Vector128 fn0331264 = Vector128.Create(-0.331264f); + Vector128 fn0418688 = Vector128.Create(-0.418688f); + Vector128 fn0081312F = Vector128.Create(-0.081312F); + Vector128 f05 = Vector128.Create(0.5f); + + nuint n = values.Component0.Vector128Count(); + for (nuint i = 0; i < n; i++) + { + Vector128 r = maxSampleValue - Unsafe.Add(ref srcR, i); + Vector128 g = maxSampleValue - Unsafe.Add(ref srcG, i); + Vector128 b = maxSampleValue - 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) + Vector128 y = Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + Vector128 cb = chromaOffset + Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r); + Vector128 cr = chromaOffset + Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r); + + 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.YccKVector256.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector256.cs new file mode 100644 index 000000000..27f2ce035 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector256.cs @@ -0,0 +1,130 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using Vector256_ = SixLabors.ImageSharp.Common.Helpers.Vector256Utilities; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class YccKVector256 : JpegColorConverterVector256 + { + public YccKVector256(int precision) + : base(JpegColorSpace.Ycck, precision) + { + } + + /// + public override void ConvertToRgbInPlace(in ComponentValues values) + { + 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 kBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + + // Used for the color conversion + Vector256 chromaOffset = Vector256.Create(-this.HalfValue); + Vector256 scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue)); + Vector256 max = Vector256.Create(this.MaximumValue); + Vector256 rCrMult = Vector256.Create(YCbCrScalar.RCrMult); + Vector256 gCbMult = Vector256.Create(-YCbCrScalar.GCbMult); + Vector256 gCrMult = Vector256.Create(-YCbCrScalar.GCrMult); + Vector256 bCbMult = Vector256.Create(YCbCrScalar.BCbMult); + + // Walking 8 elements at one step: + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) + { + // y = yVals[i]; + // cb = cbVals[i] - 128F; + // cr = crVals[i] - 128F; + // k = kVals[i] / 256F; + 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 y = c0; + Vector256 cb = c1 + chromaOffset; + Vector256 cr = c2 + chromaOffset; + Vector256 scaledK = Unsafe.Add(ref kBase, i) * scale; + + // r = y + (1.402F * cr); + // g = y - (0.344136F * cb) - (0.714136F * cr); + // b = y + (1.772F * cb); + Vector256 r = Vector256_.MultiplyAdd(y, cr, rCrMult); + Vector256 g = Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); + Vector256 b = Vector256_.MultiplyAdd(y, cb, bCbMult); + + r = max - Vector256_.RoundToNearestInteger(r); + g = max - Vector256_.RoundToNearestInteger(g); + b = max - Vector256_.RoundToNearestInteger(b); + + r *= scaledK; + g *= scaledK; + b *= scaledK; + + c0 = r; + c1 = g; + c2 = b; + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + // rgb -> cmyk + CmykVector256.ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); + + // cmyk -> ycck + ref Vector256 destY = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector256 destCb = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector256 destCr = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + ref Vector256 srcR = ref destY; + ref Vector256 srcG = ref destCb; + ref Vector256 srcB = ref destCr; + + // Used for the color conversion + Vector256 maxSampleValue = Vector256.Create(this.MaximumValue); + + Vector256 chromaOffset = Vector256.Create(this.HalfValue); + + Vector256 f0299 = Vector256.Create(0.299f); + Vector256 f0587 = Vector256.Create(0.587f); + Vector256 f0114 = Vector256.Create(0.114f); + Vector256 fn0168736 = Vector256.Create(-0.168736f); + Vector256 fn0331264 = Vector256.Create(-0.331264f); + Vector256 fn0418688 = Vector256.Create(-0.418688f); + Vector256 fn0081312F = Vector256.Create(-0.081312F); + Vector256 f05 = Vector256.Create(0.5f); + + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) + { + Vector256 r = maxSampleValue - Unsafe.Add(ref srcR, i); + Vector256 g = maxSampleValue - Unsafe.Add(ref srcG, i); + Vector256 b = maxSampleValue - 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) + Vector256 y = Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + Vector256 cb = chromaOffset + Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r); + Vector256 cr = chromaOffset + Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r); + + 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.YccKVector512.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector512.cs new file mode 100644 index 000000000..42d89a231 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector512.cs @@ -0,0 +1,144 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using Vector512_ = SixLabors.ImageSharp.Common.Helpers.Vector512Utilities; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class YccKVector512 : JpegColorConverterVector512 + { + public YccKVector512(int precision) + : base(JpegColorSpace.Ycck, precision) + { + } + + /// + protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) + { + ref Vector512 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector512 c1Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector512 c2Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector512 kBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + + // Used for the color conversion + Vector512 chromaOffset = Vector512.Create(-this.HalfValue); + Vector512 scale = Vector512.Create(1 / (this.MaximumValue * this.MaximumValue)); + Vector512 max = Vector512.Create(this.MaximumValue); + Vector512 rCrMult = Vector512.Create(YCbCrScalar.RCrMult); + Vector512 gCbMult = Vector512.Create(-YCbCrScalar.GCbMult); + Vector512 gCrMult = Vector512.Create(-YCbCrScalar.GCrMult); + Vector512 bCbMult = Vector512.Create(YCbCrScalar.BCbMult); + + // Walking 8 elements at one step: + nuint n = values.Component0.Vector512Count(); + for (nuint i = 0; i < n; i++) + { + // y = yVals[i]; + // cb = cbVals[i] - 128F; + // cr = crVals[i] - 128F; + // k = kVals[i] / 256F; + ref Vector512 c0 = ref Unsafe.Add(ref c0Base, i); + ref Vector512 c1 = ref Unsafe.Add(ref c1Base, i); + ref Vector512 c2 = ref Unsafe.Add(ref c2Base, i); + Vector512 y = c0; + Vector512 cb = c1 + chromaOffset; + Vector512 cr = c2 + chromaOffset; + Vector512 scaledK = Unsafe.Add(ref kBase, i) * scale; + + // r = y + (1.402F * cr); + // g = y - (0.344136F * cb) - (0.714136F * cr); + // b = y + (1.772F * cb); + Vector512 r = Vector512_.MultiplyAdd(y, cr, rCrMult); + Vector512 g = Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); + Vector512 b = Vector512_.MultiplyAdd(y, cb, bCbMult); + + r = max - Vector512_.RoundToNearestInteger(r); + g = max - Vector512_.RoundToNearestInteger(g); + b = max - Vector512_.RoundToNearestInteger(b); + + r *= scaledK; + g *= scaledK; + b *= scaledK; + + c0 = r; + c1 = g; + c2 = b; + } + } + + /// + protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) + => YccKScalar.ConvertToRgpInPlace(values, this.MaximumValue, this.HalfValue); + + /// + protected override void ConvertFromRgbVectorized(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + // rgb -> cmyk + CmykVector512.ConvertFromRgbVectorized(in values, this.MaximumValue, rLane, gLane, bLane); + + // cmyk -> ycck + ref Vector512 destY = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector512 destCb = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector512 destCr = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + ref Vector512 srcR = ref destY; + ref Vector512 srcG = ref destCb; + ref Vector512 srcB = ref destCr; + + // Used for the color conversion + Vector512 maxSampleValue = Vector512.Create(this.MaximumValue); + + Vector512 chromaOffset = Vector512.Create(this.HalfValue); + + Vector512 f0299 = Vector512.Create(0.299f); + Vector512 f0587 = Vector512.Create(0.587f); + Vector512 f0114 = Vector512.Create(0.114f); + Vector512 fn0168736 = Vector512.Create(-0.168736f); + Vector512 fn0331264 = Vector512.Create(-0.331264f); + Vector512 fn0418688 = Vector512.Create(-0.418688f); + Vector512 fn0081312F = Vector512.Create(-0.081312F); + Vector512 f05 = Vector512.Create(0.5f); + + nuint n = values.Component0.Vector512Count(); + for (nuint i = 0; i < n; i++) + { + Vector512 r = maxSampleValue - Unsafe.Add(ref srcR, i); + Vector512 g = maxSampleValue - Unsafe.Add(ref srcG, i); + Vector512 b = maxSampleValue - 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) + Vector512 y = Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(f0114 * b, f0587, g), f0299, r); + Vector512 cb = chromaOffset + Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r); + Vector512 cr = chromaOffset + Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r); + + Unsafe.Add(ref destY, i) = y; + Unsafe.Add(ref destCb, i) = cb; + Unsafe.Add(ref destCr, i) = cr; + } + } + + /// + protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + // rgb -> cmyk + CmykScalar.ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); + + // cmyk -> ycck + YccKScalar.ConvertFromRgb(in values, this.HalfValue, this.MaximumValue, rLane, gLane, bLane); + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs index 96eb927d0..0371c4e24 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs @@ -140,9 +140,19 @@ internal abstract partial class JpegColorConverterBase /// The precision in bits. private static JpegColorConverterBase GetYccKConverter(int precision) { - if (JpegColorConverterVector.IsSupported) + if (JpegColorConverterVector512.IsSupported) + { + return new YccKVector512(precision); + } + + if (JpegColorConverterVector256.IsSupported) + { + return new YccKVector256(precision); + } + + if (JpegColorConverterVector128.IsSupported) { - return new YccKVector(precision); + return new YccKVector128(precision); } return new YccKScalar(precision); diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs index 1a81c2190..b18528e40 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs @@ -17,7 +17,7 @@ public class YccKColorConverter : ColorConversionBenchmark [Benchmark(Baseline = true)] public void Scalar() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); new JpegColorConverterBase.YccKScalar(8).ConvertToRgbInPlace(values); } @@ -25,8 +25,32 @@ public class YccKColorConverter : ColorConversionBenchmark [Benchmark] public void SimdVector8() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); new JpegColorConverterBase.YccKVector(8).ConvertToRgbInPlace(values); } + + [Benchmark] + public void SimdVector128() + { + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); + + new JpegColorConverterBase.YccKVector128(8).ConvertToRgbInPlace(values); + } + + [Benchmark] + public void SimdVector256() + { + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); + + new JpegColorConverterBase.YccKVector256(8).ConvertToRgbInPlace(values); + } + + [Benchmark] + public void SimdVector512() + { + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); + + new JpegColorConverterBase.YccKVector512(8).ConvertToRgbInPlace(values); + } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 52b5d3183..5dc55cbe2 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -213,17 +213,17 @@ public class JpegColorConverterTests { // arrange Type expectedType = typeof(JpegColorConverterBase.YccKScalar); - if (Avx.IsSupported) + if (JpegColorConverterBase.JpegColorConverterVector512.IsSupported) { - expectedType = typeof(JpegColorConverterBase.YccKVector); + expectedType = typeof(JpegColorConverterBase.YccKVector512); } - else if (Sse2.IsSupported) + else if (JpegColorConverterBase.JpegColorConverterVector256.IsSupported) { - expectedType = typeof(JpegColorConverterBase.YccKVector); + expectedType = typeof(JpegColorConverterBase.YccKVector256); } - else if (AdvSimd.Arm64.IsSupported) + else if (JpegColorConverterBase.JpegColorConverterVector128.IsSupported) { - expectedType = typeof(JpegColorConverterBase.YccKVector); + expectedType = typeof(JpegColorConverterBase.YccKVector128); } // act @@ -474,29 +474,60 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] - public void FromYccKVector(int seed) - { - JpegColorConverterBase.YccKVector converter = new(8); + public void FromYccKVector512(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.YccKVector512(8), + 4, + seed, + new JpegColorConverterBase.YccKScalar(8)); - if (!converter.IsAvailable) - { - this.Output.WriteLine( - $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); - return; - } + [Theory] + [MemberData(nameof(Seeds))] + public void FromYccKVector256(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.YccKVector256(8), + 4, + seed, + new JpegColorConverterBase.YccKScalar(8)); - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, + [Theory] + [MemberData(nameof(Seeds))] + public void FromYccKVector128(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.YccKVector128(8), + 4, seed, - IntrinsicsConfig); + new JpegColorConverterBase.YccKScalar(8)); - static void RunTest(string arg) => - ValidateConversionToRgb( - new JpegColorConverterBase.YccKVector(8), - 4, - FeatureTestRunner.Deserialize(arg), - new JpegColorConverterBase.YccKScalar(8)); - } + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToYccKVector512(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.YccKVector512(8), + 4, + seed, + new JpegColorConverterBase.YccKScalar(8), + precision: 2); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToYccKVector256(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.YccKVector256(8), + 4, + seed, + new JpegColorConverterBase.YccKScalar(8), + precision: 2); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToYccKVector128(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.YccKVector128(8), + 4, + seed, + new JpegColorConverterBase.YccKScalar(8), + precision: 2); private void TestConversionToRgb( JpegColorConverterBase converter, From 57614df1c0590ef26246bc88573de181a088a2de Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 5 May 2025 15:02:11 +1000 Subject: [PATCH 08/11] Port RGB converter --- src/ImageSharp/Common/Helpers/SimdUtils.cs | 11 -- .../JpegColorConverter.CmykScalar.cs | 4 +- .../JpegColorConverter.CmykVector.cs | 104 -------------- .../JpegColorConverter.CmykVector512.cs | 2 +- .../JpegColorConverter.GrayScaleVector.cs | 72 ---------- .../JpegColorConverter.RgbVector.cs | 59 -------- .../JpegColorConverter.RgbVector128.cs | 51 +++++++ .../JpegColorConverter.RgbVector256.cs | 51 +++++++ .../JpegColorConverter.RgbVector512.cs | 59 ++++++++ .../JpegColorConverter.YccKVector.cs | 136 ------------------ .../ColorConverters/JpegColorConverterBase.cs | 14 +- .../ColorConversion/RgbColorConversion.cs | 24 +++- .../ColorConversion/YccKColorConverter.cs | 8 -- .../Formats/Jpg/JpegColorConverterTests.cs | 85 +++++++---- 14 files changed, 254 insertions(+), 426 deletions(-) delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector128.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector256.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector512.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs index 7f10f3182..950c8b33f 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -40,17 +40,6 @@ internal static partial class SimdUtils [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector RoundToNearestInteger(this Vector v) { - if (Avx512F.IsSupported && Vector.Count == Vector512.Count) - { - ref Vector512 v512 = ref Unsafe.As, Vector512>(ref v); - - // imm8 = 0b1000: - // imm8[7:4] = 0b0000 -> preserve 0 fractional bits (round to whole numbers) - // imm8[3:0] = 0b1000 -> _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC (round to nearest even, suppress exceptions) - Vector512 vRound = Avx512F.RoundScale(v512, 0b0000_1000); - return Unsafe.As, Vector>(ref vRound); - } - if (Avx2.IsSupported && Vector.Count == Vector256.Count) { ref Vector256 v256 = ref Unsafe.As, Vector256>(ref v); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs index 60078db38..380d3d6cc 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs @@ -14,13 +14,13 @@ internal abstract partial class JpegColorConverterBase /// public override void ConvertToRgbInPlace(in ComponentValues values) => - ConvertToRgbInplace(values, this.MaximumValue); + ConvertToRgbInPlace(values, this.MaximumValue); /// public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) => ConvertFromRgb(values, this.MaximumValue, rLane, gLane, bLane); - public static void ConvertToRgbInplace(in ComponentValues values, float maxValue) + public static void ConvertToRgbInPlace(in ComponentValues values, float maxValue) { Span c0 = values.Component0; Span c1 = values.Component1; diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs deleted file mode 100644 index 84b0b16d0..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class CmykVector : JpegColorConverterVector - { - public CmykVector(int precision) - : base(JpegColorSpace.Cmyk, precision) - { - } - - /// - protected override void ConvertToRgbInPlaceVectorized(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 * this.MaximumValue)); - - nuint n = values.Component0.VectorCount(); - for (nuint 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); - - k *= scale; - c *= k; - m *= k; - y *= k; - } - } - - /// - protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) - => CmykScalar.ConvertToRgbInplace(values, this.MaximumValue); - - /// - protected override void ConvertFromRgbVectorized(in ComponentValues values, Span r, Span g, Span b) - => ConvertFromRgbInplaceVectorized(in values, this.MaximumValue, r, g, b); - - /// - protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span r, Span g, Span b) - => ConvertFromRgbInplaceRemainder(values, this.MaximumValue, r, g, b); - - public static void ConvertFromRgbInplaceVectorized(in ComponentValues values, float maxValue, Span r, Span g, Span b) - { - ref Vector destC = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector destM = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector destY = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - 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(maxValue); - - nuint n = values.Component0.VectorCount(); - for (nuint i = 0; i < n; i++) - { - 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); - ctmp = Vector.AndNot((ctmp - ktmp) / (scale - ktmp), kMask.As()); - mtmp = Vector.AndNot((mtmp - ktmp) / (scale - ktmp), kMask.As()); - ytmp = Vector.AndNot((ytmp - ktmp) / (scale - ktmp), kMask.As()); - - 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; - } - } - - public static void ConvertFromRgbInplaceRemainder(in ComponentValues values, float maxValue, Span r, Span g, Span b) - => CmykScalar.ConvertFromRgb(values, maxValue, r, g, b); - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs index 6daf4f8b8..54ebe89df 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs @@ -52,7 +52,7 @@ internal abstract partial class JpegColorConverterBase /// protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) - => CmykScalar.ConvertToRgbInplace(values, this.MaximumValue); + => CmykScalar.ConvertToRgbInPlace(values, this.MaximumValue); /// protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span rLane, Span gLane, Span bLane) diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs deleted file mode 100644 index c9e1a3499..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class GrayScaleVector : JpegColorConverterVector - { - public GrayScaleVector(int precision) - : base(JpegColorSpace.Grayscale, precision) - { - } - - /// - protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) - { - ref Vector cBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - - var scale = new Vector(1 / this.MaximumValue); - - nuint n = values.Component0.VectorCount(); - for (nuint i = 0; i < n; i++) - { - ref Vector c0 = ref Unsafe.Add(ref cBase, i); - c0 *= scale; - } - } - - /// - protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) - => GrayScaleScalar.ConvertToRgbInPlace(values.Component0, this.MaximumValue); - - /// - protected override void ConvertFromRgbVectorized(in ComponentValues values, Span rLane, Span gLane, Span bLane) - { - ref Vector destLuma = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - - 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); - - nuint n = values.Component0.VectorCount(); - for (nuint 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 ConvertFromRgbScalarRemainder(in ComponentValues values, Span r, Span g, Span b) - => GrayScaleScalar.ConvertFromRgbScalar(values, r, g, b); - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs deleted file mode 100644 index 2b4ae2af1..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class RgbVector : JpegColorConverterVector - { - public RgbVector(int precision) - : base(JpegColorSpace.RGB, precision) - { - } - - /// - protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) - { - ref Vector rBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector gBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector bBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - - var scale = new Vector(1 / this.MaximumValue); - - nuint n = values.Component0.VectorCount(); - for (nuint i = 0; i < n; i++) - { - ref Vector r = ref Unsafe.Add(ref rBase, i); - ref Vector g = ref Unsafe.Add(ref gBase, i); - ref Vector b = ref Unsafe.Add(ref bBase, i); - r *= scale; - g *= scale; - b *= scale; - } - } - - /// - protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) - => RgbScalar.ConvertToRgbInPlace(values, this.MaximumValue); - - /// - protected override void ConvertFromRgbVectorized(in ComponentValues values, Span r, Span g, Span b) - { - r.CopyTo(values.Component0); - g.CopyTo(values.Component1); - b.CopyTo(values.Component2); - } - - /// - protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span r, Span g, Span b) - => RgbScalar.ConvertFromRgb(values, r, g, b); - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector128.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector128.cs new file mode 100644 index 000000000..47aa4281b --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector128.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class RgbVector128 : JpegColorConverterVector128 + { + public RgbVector128(int precision) + : base(JpegColorSpace.RGB, precision) + { + } + + /// + public override void ConvertToRgbInPlace(in ComponentValues values) + { + ref Vector128 rBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 gBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 bBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + // Used for the color conversion + Vector128 scale = Vector128.Create(1 / this.MaximumValue); + nuint n = values.Component0.Vector128Count(); + for (nuint i = 0; i < n; i++) + { + ref Vector128 r = ref Unsafe.Add(ref rBase, i); + ref Vector128 g = ref Unsafe.Add(ref gBase, i); + ref Vector128 b = ref Unsafe.Add(ref bBase, i); + r *= scale; + g *= scale; + b *= scale; + } + } + + /// + public override void ConvertFromRgb(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.RgbVector256.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector256.cs new file mode 100644 index 000000000..02448d724 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector256.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class RgbVector256 : JpegColorConverterVector256 + { + public RgbVector256(int precision) + : base(JpegColorSpace.RGB, precision) + { + } + + /// + public override void ConvertToRgbInPlace(in ComponentValues values) + { + ref Vector256 rBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector256 gBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector256 bBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + // Used for the color conversion + Vector256 scale = Vector256.Create(1 / this.MaximumValue); + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) + { + ref Vector256 r = ref Unsafe.Add(ref rBase, i); + ref Vector256 g = ref Unsafe.Add(ref gBase, i); + ref Vector256 b = ref Unsafe.Add(ref bBase, i); + r *= scale; + g *= scale; + b *= scale; + } + } + + /// + public override void ConvertFromRgb(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.RgbVector512.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector512.cs new file mode 100644 index 000000000..76745f665 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector512.cs @@ -0,0 +1,59 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class RgbVector512 : JpegColorConverterVector512 + { + public RgbVector512(int precision) + : base(JpegColorSpace.RGB, precision) + { + } + + /// + protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) + { + ref Vector512 rBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector512 gBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector512 bBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + // Used for the color conversion + Vector512 scale = Vector512.Create(1 / this.MaximumValue); + nuint n = values.Component0.Vector512Count(); + for (nuint i = 0; i < n; i++) + { + ref Vector512 r = ref Unsafe.Add(ref rBase, i); + ref Vector512 g = ref Unsafe.Add(ref gBase, i); + ref Vector512 b = ref Unsafe.Add(ref bBase, i); + r *= scale; + g *= scale; + b *= scale; + } + } + + /// + protected override void ConvertFromRgbVectorized(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + rLane.CopyTo(values.Component0); + gLane.CopyTo(values.Component1); + bLane.CopyTo(values.Component2); + } + + /// + protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) + => RgbScalar.ConvertToRgbInPlace(values, this.MaximumValue); + + /// + protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => RgbScalar.ConvertFromRgb(values, rLane, gLane, bLane); + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs deleted file mode 100644 index af144240e..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components; - -internal abstract partial class JpegColorConverterBase -{ - internal sealed class YccKVector : JpegColorConverterVector - { - public YccKVector(int precision) - : base(JpegColorSpace.Ycck, precision) - { - } - - /// - protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) - { - ref Vector c0Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector c1Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector c2Base = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - ref Vector kBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); - - var chromaOffset = new Vector(-this.HalfValue); - var scale = new Vector(1 / (this.MaximumValue * this.MaximumValue)); - var max = new Vector(this.MaximumValue); - var rCrMult = new Vector(YCbCrScalar.RCrMult); - var gCbMult = new Vector(-YCbCrScalar.GCbMult); - var gCrMult = new Vector(-YCbCrScalar.GCrMult); - var bCbMult = new Vector(YCbCrScalar.BCbMult); - - nuint n = values.Component0.VectorCount(); - for (nuint i = 0; i < n; i++) - { - // y = yVals[i]; - // cb = cbVals[i] - 128F; - // cr = crVals[i] - 128F; - // k = kVals[i] / 256F; - 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 y = c0; - Vector cb = c1 + chromaOffset; - Vector cr = c2 + chromaOffset; - Vector scaledK = Unsafe.Add(ref kBase, i) * scale; - - // r = y + (1.402F * cr); - // g = y - (0.344136F * cb) - (0.714136F * cr); - // b = y + (1.772F * cb); - Vector r = y + (cr * rCrMult); - Vector g = y + (cb * gCbMult) + (cr * gCrMult); - Vector b = y + (cb * bCbMult); - - r = (max - r.RoundToNearestInteger()) * scaledK; - g = (max - g.RoundToNearestInteger()) * scaledK; - b = (max - b.RoundToNearestInteger()) * scaledK; - - c0 = r; - c1 = g; - c2 = b; - } - } - - /// - protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) - => YccKScalar.ConvertToRgpInPlace(values, this.MaximumValue, this.HalfValue); - - /// - protected override void ConvertFromRgbVectorized(in ComponentValues values, Span rLane, Span gLane, Span bLane) - { - // rgb -> cmyk - CmykVector.ConvertFromRgbInplaceVectorized(in values, this.MaximumValue, rLane, gLane, bLane); - - // cmyk -> ycck - ref Vector destY = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); - ref Vector destCb = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); - ref Vector destCr = - ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); - - ref Vector srcR = ref destY; - ref Vector srcG = ref destCb; - ref Vector srcB = ref destCr; - - var maxSampleValue = new Vector(this.MaximumValue); - - var chromaOffset = new Vector(this.HalfValue); - - var rYMult = new Vector(0.299f); - var gYMult = new Vector(0.587f); - var bYMult = new Vector(0.114f); - - var rCbMult = new Vector(0.168736f); - var gCbMult = new Vector(0.331264f); - var bCbMult = new Vector(0.5f); - - var rCrMult = new Vector(0.5f); - var gCrMult = new Vector(0.418688f); - var bCrMult = new Vector(0.081312f); - - nuint n = values.Component0.VectorCount(); - for (nuint i = 0; i < n; i++) - { - Vector r = maxSampleValue - Unsafe.Add(ref srcR, i); - Vector g = maxSampleValue - Unsafe.Add(ref srcG, i); - Vector b = maxSampleValue - 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) - 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 ConvertFromRgbScalarRemainder(in ComponentValues values, Span r, Span g, Span b) - { - // rgb -> cmyk - CmykScalar.ConvertFromRgb(in values, this.MaximumValue, r, g, b); - - // cmyk -> ycck - YccKScalar.ConvertFromRgb(in values, this.HalfValue, this.MaximumValue, r, g, b); - } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs index 0371c4e24..8cb3045dc 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs @@ -212,9 +212,19 @@ internal abstract partial class JpegColorConverterBase /// The precision in bits. private static JpegColorConverterBase GetRgbConverter(int precision) { - if (JpegColorConverterVector.IsSupported) + if (JpegColorConverterVector512.IsSupported) + { + return new RgbVector512(precision); + } + + if (JpegColorConverterVector256.IsSupported) + { + return new RgbVector256(precision); + } + + if (JpegColorConverterVector128.IsSupported) { - return new RgbVector(precision); + return new RgbVector128(precision); } return new RgbScalar(precision); diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs index 62bf75d42..2916dcdce 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs @@ -17,16 +17,32 @@ public class RgbColorConversion : ColorConversionBenchmark [Benchmark(Baseline = true)] public void Scalar() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); new JpegColorConverterBase.RgbScalar(8).ConvertToRgbInPlace(values); } [Benchmark] - public void SimdVector8() + public void SimdVector128() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); - new JpegColorConverterBase.RgbVector(8).ConvertToRgbInPlace(values); + new JpegColorConverterBase.RgbVector128(8).ConvertToRgbInPlace(values); + } + + [Benchmark] + public void SimdVector256() + { + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); + + new JpegColorConverterBase.RgbVector256(8).ConvertToRgbInPlace(values); + } + + [Benchmark] + public void SimdVector512() + { + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); + + new JpegColorConverterBase.RgbVector512(8).ConvertToRgbInPlace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs index b18528e40..e6b04c152 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs @@ -22,14 +22,6 @@ public class YccKColorConverter : ColorConversionBenchmark new JpegColorConverterBase.YccKScalar(8).ConvertToRgbInPlace(values); } - [Benchmark] - public void SimdVector8() - { - JpegColorConverterBase.ComponentValues values = new(this.Input, 0); - - new JpegColorConverterBase.YccKVector(8).ConvertToRgbInPlace(values); - } - [Benchmark] public void SimdVector128() { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 5dc55cbe2..25e605cef 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -75,23 +75,23 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { // arrange Type expectedType = typeof(JpegColorConverterBase.RgbScalar); - if (Avx.IsSupported) + if (JpegColorConverterBase.JpegColorConverterVector512.IsSupported) { - expectedType = typeof(JpegColorConverterBase.RgbVector); + expectedType = typeof(JpegColorConverterBase.RgbVector512); } - else if (Sse2.IsSupported) + else if (JpegColorConverterBase.JpegColorConverterVector256.IsSupported) { - expectedType = typeof(JpegColorConverterBase.RgbVector); + expectedType = typeof(JpegColorConverterBase.RgbVector256); } - else if (AdvSimd.IsSupported) + else if (JpegColorConverterBase.JpegColorConverterVector128.IsSupported) { - expectedType = typeof(JpegColorConverterBase.RgbVector); + expectedType = typeof(JpegColorConverterBase.RgbVector128); } // act @@ -443,29 +443,60 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] - public void FromRgbVector(int seed) - { - JpegColorConverterBase.RgbVector converter = new(8); + public void FromRgbVector512(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.RgbVector512(8), + 3, + seed, + new JpegColorConverterBase.RgbScalar(8)); - if (!converter.IsAvailable) - { - this.Output.WriteLine( - $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); - return; - } + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbVector256(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.RgbVector256(8), + 3, + seed, + new JpegColorConverterBase.RgbScalar(8)); - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbVector128(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.RgbVector128(8), + 3, seed, - IntrinsicsConfig); - - static void RunTest(string arg) => - ValidateConversionToRgb( - new JpegColorConverterBase.RgbVector(8), - 3, - FeatureTestRunner.Deserialize(arg), - new JpegColorConverterBase.RgbScalar(8)); - } + new JpegColorConverterBase.RgbScalar(8)); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToRgbVector512(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.RgbVector512(8), + 3, + seed, + new JpegColorConverterBase.RgbScalar(8), + precision: 2); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToRgbVector256(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.RgbVector256(8), + 3, + seed, + new JpegColorConverterBase.RgbScalar(8), + precision: 2); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToRgbVector128(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.RgbVector128(8), + 3, + seed, + new JpegColorConverterBase.RgbScalar(8), + precision: 2); [Theory] [MemberData(nameof(Seeds))] From 71ca954e3575d25bf287cb0d7dc7056e3f867b73 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 5 May 2025 15:24:35 +1000 Subject: [PATCH 09/11] Revert naming --- src/ImageSharp/Common/Helpers/SimdUtils.cs | 2 +- .../Formats/Jpeg/Components/Block8x8F.cs | 2 +- .../Jpeg/BlockOperations/Block8x8F_Round.cs | 48 +++++++++---------- .../ImageSharp.Tests/Common/SimdUtilsTests.cs | 4 +- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs index 950c8b33f..ea5a4c07e 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -38,7 +38,7 @@ internal static partial class SimdUtils /// /// The vector [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector RoundToNearestInteger(this Vector v) + internal static Vector FastRound(this Vector v) { if (Avx2.IsSupported && Vector.Count == Vector256.Count) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index aa9249be5..018df5f9f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -588,6 +588,6 @@ internal partial struct Block8x8F : IEquatable row += off; row = Vector.Max(row, Vector.Zero); row = Vector.Min(row, max); - return row.RoundToNearestInteger(); + return row.FastRound(); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs index 92faa50d2..1d8385168 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs @@ -67,21 +67,21 @@ public unsafe class Block8x8F_Round ref Block8x8F b = ref this.block; ref Vector row0 = ref Unsafe.As>(ref b.V0L); - row0 = row0.RoundToNearestInteger(); + row0 = row0.FastRound(); ref Vector row1 = ref Unsafe.As>(ref b.V1L); - row1 = row1.RoundToNearestInteger(); + row1 = row1.FastRound(); ref Vector row2 = ref Unsafe.As>(ref b.V2L); - row2 = row2.RoundToNearestInteger(); + row2 = row2.FastRound(); ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row3 = row3.RoundToNearestInteger(); + row3 = row3.FastRound(); ref Vector row4 = ref Unsafe.As>(ref b.V4L); - row4 = row4.RoundToNearestInteger(); + row4 = row4.FastRound(); ref Vector row5 = ref Unsafe.As>(ref b.V5L); - row5 = row5.RoundToNearestInteger(); + row5 = row5.FastRound(); ref Vector row6 = ref Unsafe.As>(ref b.V6L); - row6 = row6.RoundToNearestInteger(); + row6 = row6.FastRound(); ref Vector row7 = ref Unsafe.As>(ref b.V7L); - row7 = row7.RoundToNearestInteger(); + row7 = row7.FastRound(); } [Benchmark] @@ -90,21 +90,21 @@ public unsafe class Block8x8F_Round ref Block8x8F b = ref Unsafe.AsRef(this.alignedPtr); ref Vector row0 = ref Unsafe.As>(ref b.V0L); - row0 = row0.RoundToNearestInteger(); + row0 = row0.FastRound(); ref Vector row1 = ref Unsafe.As>(ref b.V1L); - row1 = row1.RoundToNearestInteger(); + row1 = row1.FastRound(); ref Vector row2 = ref Unsafe.As>(ref b.V2L); - row2 = row2.RoundToNearestInteger(); + row2 = row2.FastRound(); ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row3 = row3.RoundToNearestInteger(); + row3 = row3.FastRound(); ref Vector row4 = ref Unsafe.As>(ref b.V4L); - row4 = row4.RoundToNearestInteger(); + row4 = row4.FastRound(); ref Vector row5 = ref Unsafe.As>(ref b.V5L); - row5 = row5.RoundToNearestInteger(); + row5 = row5.FastRound(); ref Vector row6 = ref Unsafe.As>(ref b.V6L); - row6 = row6.RoundToNearestInteger(); + row6 = row6.FastRound(); ref Vector row7 = ref Unsafe.As>(ref b.V7L); - row7 = row7.RoundToNearestInteger(); + row7 = row7.FastRound(); } [Benchmark] @@ -117,20 +117,20 @@ public unsafe class Block8x8F_Round ref Vector row2 = ref Unsafe.As>(ref b.V2L); ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row0 = row0.RoundToNearestInteger(); - row1 = row1.RoundToNearestInteger(); - row2 = row2.RoundToNearestInteger(); - row3 = row3.RoundToNearestInteger(); + row0 = row0.FastRound(); + row1 = row1.FastRound(); + row2 = row2.FastRound(); + row3 = row3.FastRound(); row0 = ref Unsafe.As>(ref b.V4L); row1 = ref Unsafe.As>(ref b.V5L); row2 = ref Unsafe.As>(ref b.V6L); row3 = ref Unsafe.As>(ref b.V7L); - row0 = row0.RoundToNearestInteger(); - row1 = row1.RoundToNearestInteger(); - row2 = row2.RoundToNearestInteger(); - row3 = row3.RoundToNearestInteger(); + row0 = row0.FastRound(); + row1 = row1.FastRound(); + row2 = row2.FastRound(); + row3 = row3.FastRound(); } [Benchmark] diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index 77c9889ae..36b301264 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -73,7 +73,7 @@ public partial class SimdUtilsTests public void FastRound() { Vector v = CreateExactTestVector1(); - Vector r = v.RoundToNearestInteger(); + Vector r = v.FastRound(); this.Output.WriteLine(r.ToString()); @@ -90,7 +90,7 @@ public partial class SimdUtilsTests public void FastRound_RandomValues(int seed, float scale) { Vector v = CreateRandomTestVector(seed, -scale * 0.5f, scale * 0.5f); - Vector r = v.RoundToNearestInteger(); + Vector r = v.FastRound(); this.Output.WriteLine(v.ToString()); this.Output.WriteLine(r.ToString()); From 277cf61b75abdbcdd73c719736a7eff18f881347 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 5 May 2025 16:04:18 +1000 Subject: [PATCH 10/11] Remove unused method --- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index da93e1500..a0733b660 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -616,44 +616,6 @@ internal static partial class SimdUtils return Fma.MultiplyAdd(vm1, vm0, va); } - if (Avx.IsSupported) - { - return Avx.Add(Avx.Multiply(vm0, vm1), va); - } - - return va + (vm0 * vm1); - } - - /// - /// Performs a multiplication and an addition of the . - /// TODO: Fix. The arguments are in a different order to the FMA intrinsic. - /// - /// ret = (vm0 * vm1) + va - /// The vector to add to the intermediate result. - /// The first vector to multiply. - /// The second vector to multiply. - /// The . - [MethodImpl(InliningOptions.AlwaysInline)] - public static Vector128 MultiplyAdd( - Vector128 va, - Vector128 vm0, - Vector128 vm1) - { - if (Fma.IsSupported) - { - return Fma.MultiplyAdd(vm1, vm0, va); - } - - if (AdvSimd.IsSupported) - { - return AdvSimd.Add(AdvSimd.Multiply(vm0, vm1), va); - } - - if (Sse.IsSupported) - { - return Sse.Add(Sse.Multiply(vm0, vm1), va); - } - return va + (vm0 * vm1); } From 5b94795559c90869267d14285f41063cb7b4b5ff Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 May 2025 09:49:50 +1000 Subject: [PATCH 11/11] Address feedback --- src/ImageSharp/Common/Helpers/SimdUtils.cs | 16 ++++++++++++++++ .../Common/Helpers/Vector128Utilities.cs | 5 +++++ .../Common/Helpers/Vector256Utilities.cs | 5 ----- .../JpegColorConverter.CmykVector128.cs | 10 ++++++---- .../JpegColorConverter.CmykVector256.cs | 10 ++++++---- .../JpegColorConverter.CmykVector512.cs | 10 ++++++---- .../JpegColorConverterVector128.cs | 2 +- .../JpegColorConverterVector256.cs | 2 +- .../JpegColorConverterVector512.cs | 2 +- 9 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs index ea5a4c07e..7f98c8375 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp; @@ -40,6 +41,7 @@ internal static partial class SimdUtils [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector FastRound(this Vector v) { + // .NET9+ has a built-in method for this Vector.Round if (Avx2.IsSupported && Vector.Count == Vector256.Count) { ref Vector256 v256 = ref Unsafe.As, Vector256>(ref v); @@ -47,6 +49,20 @@ internal static partial class SimdUtils return Unsafe.As, Vector>(ref vRound); } + if (Sse41.IsSupported && Vector.Count == Vector128.Count) + { + ref Vector128 v128 = ref Unsafe.As, Vector128>(ref v); + Vector128 vRound = Sse41.RoundToNearestInteger(v128); + return Unsafe.As, Vector>(ref vRound); + } + + if (AdvSimd.IsSupported && Vector.Count == Vector128.Count) + { + ref Vector128 v128 = ref Unsafe.As, Vector128>(ref v); + Vector128 vRound = AdvSimd.RoundToNearest(v128); + return Unsafe.As, Vector>(ref vRound); + } + // https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L11 Vector sign = v & new Vector(-0F); Vector val_2p23_f32 = sign | new Vector(8388608F); diff --git a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs index 6b0581851..765737906 100644 --- a/src/ImageSharp/Common/Helpers/Vector128Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector128Utilities.cs @@ -244,6 +244,11 @@ internal static class Vector128Utilities return Fma.MultiplyAdd(vm1, vm0, va); } + if (AdvSimd.IsSupported) + { + return AdvSimd.FusedMultiplyAdd(va, vm0, vm1); + } + return va + (vm0 * vm1); } diff --git a/src/ImageSharp/Common/Helpers/Vector256Utilities.cs b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs index 9928b529e..4c12cb272 100644 --- a/src/ImageSharp/Common/Helpers/Vector256Utilities.cs +++ b/src/ImageSharp/Common/Helpers/Vector256Utilities.cs @@ -149,11 +149,6 @@ internal static class Vector256Utilities return Fma.MultiplyAdd(vm0, vm1, va); } - if (Avx.IsSupported) - { - return Avx.Add(Avx.Multiply(vm0, vm1), va); - } - return va + (vm0 * vm1); } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector128.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector128.cs index 3b4c6d484..0a935cca4 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector128.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector128.cs @@ -78,10 +78,12 @@ internal abstract partial class JpegColorConverterBase Vector128 ytmp = scale - Unsafe.Add(ref srcB, i); Vector128 ktmp = Vector128.Min(ctmp, Vector128.Min(mtmp, ytmp)); - Vector128 kMask = Vector128.Equals(ktmp, scale); - ctmp = Vector128.AndNot((ctmp - ktmp) / (scale - ktmp), kMask); - mtmp = Vector128.AndNot((mtmp - ktmp) / (scale - ktmp), kMask); - ytmp = Vector128.AndNot((ytmp - ktmp) / (scale - ktmp), kMask); + Vector128 kMask = ~Vector128.Equals(ktmp, scale); + Vector128 divisor = scale - ktmp; + + ctmp = ((ctmp - ktmp) / divisor) & kMask; + mtmp = ((mtmp - ktmp) / divisor) & kMask; + ytmp = ((ytmp - ktmp) / divisor) & kMask; Unsafe.Add(ref destC, i) = scale - (ctmp * scale); Unsafe.Add(ref destM, i) = scale - (mtmp * scale); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector256.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector256.cs index 77531c8fc..3cef262ec 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector256.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector256.cs @@ -78,10 +78,12 @@ internal abstract partial class JpegColorConverterBase Vector256 ytmp = scale - Unsafe.Add(ref srcB, i); Vector256 ktmp = Vector256.Min(ctmp, Vector256.Min(mtmp, ytmp)); - Vector256 kMask = Vector256.Equals(ktmp, scale); - ctmp = Vector256.AndNot((ctmp - ktmp) / (scale - ktmp), kMask); - mtmp = Vector256.AndNot((mtmp - ktmp) / (scale - ktmp), kMask); - ytmp = Vector256.AndNot((ytmp - ktmp) / (scale - ktmp), kMask); + Vector256 kMask = ~Vector256.Equals(ktmp, scale); + Vector256 divisor = scale - ktmp; + + ctmp = ((ctmp - ktmp) / divisor) & kMask; + mtmp = ((mtmp - ktmp) / divisor) & kMask; + ytmp = ((ytmp - ktmp) / divisor) & kMask; Unsafe.Add(ref destC, i) = scale - (ctmp * scale); Unsafe.Add(ref destM, i) = scale - (mtmp * scale); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs index 54ebe89df..f57ad4352 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs @@ -86,10 +86,12 @@ internal abstract partial class JpegColorConverterBase Vector512 ytmp = scale - Unsafe.Add(ref srcB, i); Vector512 ktmp = Vector512.Min(ctmp, Vector512.Min(mtmp, ytmp)); - Vector512 kMask = Vector512.Equals(ktmp, scale); - ctmp = Vector512.AndNot((ctmp - ktmp) / (scale - ktmp), kMask); - mtmp = Vector512.AndNot((mtmp - ktmp) / (scale - ktmp), kMask); - ytmp = Vector512.AndNot((ytmp - ktmp) / (scale - ktmp), kMask); + Vector512 kMask = ~Vector512.Equals(ktmp, scale); + Vector512 divisor = scale - ktmp; + + ctmp = ((ctmp - ktmp) / divisor) & kMask; + mtmp = ((mtmp - ktmp) / divisor) & kMask; + ytmp = ((ytmp - ktmp) / divisor) & kMask; Unsafe.Add(ref destC, i) = scale - (ctmp * scale); Unsafe.Add(ref destM, i) = scale - (mtmp * scale); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector128.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector128.cs index 35458a9a0..5cbb376c7 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector128.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector128.cs @@ -25,7 +25,7 @@ internal abstract partial class JpegColorConverterBase { } - public static bool IsSupported => Vector128.IsHardwareAccelerated && Vector128.IsSupported; + public static bool IsSupported => Vector128.IsHardwareAccelerated; public sealed override bool IsAvailable => IsSupported; diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector256.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector256.cs index 14442383a..61c37d846 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector256.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector256.cs @@ -25,7 +25,7 @@ internal abstract partial class JpegColorConverterBase { } - public static bool IsSupported => Vector256.IsHardwareAccelerated && Vector256.IsSupported; + public static bool IsSupported => Vector256.IsHardwareAccelerated; public sealed override bool IsAvailable => IsSupported; diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector512.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector512.cs index e916ea5aa..0c7d032d4 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector512.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector512.cs @@ -19,7 +19,7 @@ internal abstract partial class JpegColorConverterBase { } - public static bool IsSupported => Vector512.IsHardwareAccelerated && Vector512.IsSupported; + public static bool IsSupported => Vector512.IsHardwareAccelerated; /// public override bool IsAvailable => IsSupported;