diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx2.cs index ea0132e1e..bd786b826 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx2.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx2.cs @@ -103,8 +103,72 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters #endif } + protected override void ConvertCoreVectorizedInplace(in ComponentValues values) + { +#if SUPPORTS_RUNTIME_INTRINSICS + ref Vector256 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector256 c1Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector256 c2Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector256 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(1.402F); + var gCbMult = Vector256.Create(-0.344136F); + var gCrMult = Vector256.Create(-0.714136F); + var bCbMult = Vector256.Create(1.772F); + + // Walking 8 elements at one step: + int n = values.Component0.Length / 8; + for (int 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); + // Adding & multiplying 8 elements at one time: + 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; + } +#endif + } + protected override void ConvertCore(in ComponentValues values, Span result) => FromYccKBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); + + protected override void ConvertCoreInplace(in ComponentValues values) => + FromYccKBasic.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKBasic.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKBasic.cs index 778e5325f..b0cde6971 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKBasic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKBasic.cs @@ -20,6 +20,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters ConvertCore(values, result, this.MaximumValue, this.HalfValue); } + public override void ConvertToRgbInplace(in ComponentValues values) => + ConvertCoreInplace(values, this.MaximumValue, this.HalfValue); + internal static void ConvertCore(in ComponentValues values, Span result, float maxValue, float halfValue) { // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()! @@ -50,6 +53,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters result[i] = v; } } + + internal static void ConvertCoreInplace(in ComponentValues values, float maxValue, float halfValue) + { + Span c0 = values.Component0; + Span c1 = values.Component1; + Span c2 = values.Component2; + Span c3 = values.Component3; + + var v = new Vector4(0, 0, 0, 1F); + + var scale = 1 / (maxValue * maxValue); + + for (int i = 0; i < values.Component0.Length; i++) + { + float y = c0[i]; + float cb = c1[i] - halfValue; + float cr = c2[i] - halfValue; + float scaledK = c3[i] * scale; + + c0[i] = (maxValue - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * scaledK; + c1[i] = (maxValue - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * scaledK; + c2[i] = (maxValue - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * scaledK; + } + } } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector8.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector8.cs index c360392de..64f29f1f8 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector8.cs @@ -84,8 +84,63 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters } } + protected override void ConvertCoreVectorizedInplace(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); + + // Walking 8 elements at one step: + int n = values.Component0.Length / 8; + + var scale = new Vector(1 / (this.MaximumValue * this.MaximumValue)); + var max = new Vector(this.MaximumValue); + + for (int 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); + // Adding & multiplying 8 elements at one time: + Vector r = y + (cr * new Vector(1.402F)); + Vector g = y - (cb * new Vector(0.344136F)) - (cr * new Vector(0.714136F)); + Vector b = y + (cb * new Vector(1.772F)); + + r = (max - r.FastRound()) * scaledK; + g = (max - g.FastRound()) * scaledK; + b = (max - b.FastRound()) * scaledK; + + c0 = r; + c1 = g; + c2 = b; + } + } + protected override void ConvertCore(in ComponentValues values, Span result) => FromYccKBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); + + protected override void ConvertCoreInplace(in ComponentValues values) => + FromYccKBasic.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue); } } }