Browse Source

Reduce JPEG color conversion to two pipelines

pull/2917/head
James Jackson-South 9 months ago
parent
commit
aebe375e2c
  1. 38
      src/ImageSharp/Common/Helpers/SimdUtils.cs
  2. 95
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykArm64.cs
  3. 95
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs
  4. 2
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs
  5. 68
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs
  6. 68
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs
  7. 2
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs
  8. 53
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs
  9. 52
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs
  10. 2
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs
  11. 122
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrArm.cs
  12. 121
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs
  13. 2
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs
  14. 36
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs
  15. 133
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKArm64.cs
  16. 132
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs
  17. 2
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs
  18. 35
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs
  19. 35
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm64.cs
  20. 34
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterAvx.cs
  21. 123
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs
  22. 2
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs
  23. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
  24. 20
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs
  25. 14
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs
  26. 20
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs
  27. 20
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs
  28. 20
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs
  29. 218
      tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs

38
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. // Licensed under the Six Labors Split License.
using System.Diagnostics; using System.Diagnostics;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
@ -36,30 +35,37 @@ internal static partial class SimdUtils
/// <summary> /// <summary>
/// Rounds all values in 'v' to the nearest integer following <see cref="MidpointRounding.ToEven"/> semantics. /// Rounds all values in 'v' to the nearest integer following <see cref="MidpointRounding.ToEven"/> semantics.
/// Source:
/// <see>
/// <cref>https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L110</cref>
/// </see>
/// </summary> /// </summary>
/// <param name="v">The vector</param> /// <param name="v">The vector</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector<float> FastRound(this Vector<float> v) internal static Vector<float> FastRound(this Vector<float> v)
{ {
if (Avx2.IsSupported) if (Avx512F.IsSupported && Vector<float>.Count == Vector512<float>.Count)
{
ref Vector512<float> v512 = ref Unsafe.As<Vector<float>, Vector512<float>>(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<float> vRound = Avx512F.RoundScale(v512, 0b0000_1000);
return Unsafe.As<Vector512<float>, Vector<float>>(ref vRound);
}
if (Avx2.IsSupported && Vector<float>.Count == Vector256<float>.Count)
{ {
ref Vector256<float> v256 = ref Unsafe.As<Vector<float>, Vector256<float>>(ref v); ref Vector256<float> v256 = ref Unsafe.As<Vector<float>, Vector256<float>>(ref v);
Vector256<float> vRound = Avx.RoundToNearestInteger(v256); Vector256<float> vRound = Avx.RoundToNearestInteger(v256);
return Unsafe.As<Vector256<float>, Vector<float>>(ref vRound); return Unsafe.As<Vector256<float>, Vector<float>>(ref vRound);
} }
else
{ // https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L11
var magic0 = new Vector<int>(int.MinValue); // 0x80000000 Vector<int> magic0 = new(int.MinValue); // 0x80000000
var sgn0 = Vector.AsVectorSingle(magic0); Vector<float> sgn0 = Vector.AsVectorSingle(magic0);
var and0 = Vector.BitwiseAnd(sgn0, v); Vector<float> and0 = Vector.BitwiseAnd(sgn0, v);
var or0 = Vector.BitwiseOr(and0, new Vector<float>(8388608.0f)); Vector<float> or0 = Vector.BitwiseOr(and0, new Vector<float>(8388608.0f));
var add0 = Vector.Add(v, or0); Vector<float> add0 = Vector.Add(v, or0);
return Vector.Subtract(add0, or0);
} return Vector.Subtract(add0, or0);
} }
[Conditional("DEBUG")] [Conditional("DEBUG")]

95
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykArm64.cs

@ -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)
{
}
/// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector128<float> c0Base =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector128<float> c1Base =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector128<float> c2Base =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector128<float> c3Base =
ref Unsafe.As<float, Vector128<float>>(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<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector128<float> c = ref Unsafe.Add(ref c0Base, i);
ref Vector128<float> m = ref Unsafe.Add(ref c1Base, i);
ref Vector128<float> y = ref Unsafe.Add(ref c2Base, i);
Vector128<float> 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);
}
}
/// <inheritdoc/>
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
=> ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane);
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
ref Vector128<float> destC =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector128<float> destM =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector128<float> destY =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector128<float> destK =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component3));
ref Vector128<float> srcR =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(rLane));
ref Vector128<float> srcG =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(gLane));
ref Vector128<float> srcB =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(bLane));
var scale = Vector128.Create(maxValue);
nint n = (nint)(uint)values.Component0.Length / Vector128<float>.Count;
for (nint i = 0; i < n; i++)
{
Vector128<float> ctmp = AdvSimd.Subtract(scale, Unsafe.Add(ref srcR, i));
Vector128<float> mtmp = AdvSimd.Subtract(scale, Unsafe.Add(ref srcG, i));
Vector128<float> ytmp = AdvSimd.Subtract(scale, Unsafe.Add(ref srcB, i));
Vector128<float> ktmp = AdvSimd.Min(ctmp, AdvSimd.Min(mtmp, ytmp));
Vector128<float> 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);
}
}
}
}

95
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs

@ -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)
{
}
/// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> c1Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> c2Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector256<float> c3Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3));
// Used for the color conversion
var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue));
nuint n = values.Component0.Vector256Count<float>();
for (nuint i = 0; i < n; i++)
{
ref Vector256<float> c = ref Unsafe.Add(ref c0Base, i);
ref Vector256<float> m = ref Unsafe.Add(ref c1Base, i);
ref Vector256<float> y = ref Unsafe.Add(ref c2Base, i);
Vector256<float> 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);
}
}
/// <inheritdoc/>
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
=> ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane);
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
ref Vector256<float> destC =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> destM =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> destY =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector256<float> destK =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3));
ref Vector256<float> srcR =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(rLane));
ref Vector256<float> srcG =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(gLane));
ref Vector256<float> srcB =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(bLane));
var scale = Vector256.Create(maxValue);
nuint n = values.Component0.Vector256Count<float>();
for (nuint i = 0; i < n; i++)
{
Vector256<float> ctmp = Avx.Subtract(scale, Unsafe.Add(ref srcR, i));
Vector256<float> mtmp = Avx.Subtract(scale, Unsafe.Add(ref srcG, i));
Vector256<float> ytmp = Avx.Subtract(scale, Unsafe.Add(ref srcB, i));
Vector256<float> ktmp = Avx.Min(ctmp, Avx.Min(mtmp, ytmp));
Vector256<float> 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);
}
}
}
}

2
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs

@ -13,7 +13,7 @@ internal abstract partial class JpegColorConverterBase
} }
/// <inheritdoc/> /// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values) => public override void ConvertToRgbInPlace(in ComponentValues values) =>
ConvertToRgbInplace(values, this.MaximumValue); ConvertToRgbInplace(values, this.MaximumValue);
/// <inheritdoc/> /// <inheritdoc/>

68
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs

@ -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)
{
}
/// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector128<float> c0Base =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
// Used for the color conversion
var scale = Vector128.Create(1 / this.MaximumValue);
nuint n = values.Component0.Vector128Count<float>();
for (nuint i = 0; i < n; i++)
{
ref Vector128<float> c0 = ref Unsafe.Add(ref c0Base, i);
c0 = AdvSimd.Multiply(c0, scale);
}
}
/// <inheritdoc/>
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
ref Vector128<float> destLuminance =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector128<float> srcRed =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(rLane));
ref Vector128<float> srcGreen =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(gLane));
ref Vector128<float> srcBlue =
ref Unsafe.As<float, Vector128<float>>(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<float>();
for (nuint i = 0; i < n; i++)
{
ref Vector128<float> r = ref Unsafe.Add(ref srcRed, i);
ref Vector128<float> g = ref Unsafe.Add(ref srcGreen, i);
ref Vector128<float> 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);
}
}
}
}

68
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs

@ -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)
{
}
/// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
// Used for the color conversion
var scale = Vector256.Create(1 / this.MaximumValue);
nuint n = values.Component0.Vector256Count<float>();
for (nuint i = 0; i < n; i++)
{
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i);
c0 = Avx.Multiply(c0, scale);
}
}
/// <inheritdoc/>
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
ref Vector256<float> destLuminance =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> srcRed =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(rLane));
ref Vector256<float> srcGreen =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(gLane));
ref Vector256<float> srcBlue =
ref Unsafe.As<float, Vector256<float>>(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<float>();
for (nuint i = 0; i < n; i++)
{
ref Vector256<float> r = ref Unsafe.Add(ref srcRed, i);
ref Vector256<float> g = ref Unsafe.Add(ref srcGreen, i);
ref Vector256<float> 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);
}
}
}
}

2
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs

@ -16,7 +16,7 @@ internal abstract partial class JpegColorConverterBase
} }
/// <inheritdoc/> /// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values) public override void ConvertToRgbInPlace(in ComponentValues values)
=> ConvertToRgbInplace(values.Component0, this.MaximumValue); => ConvertToRgbInplace(values.Component0, this.MaximumValue);
/// <inheritdoc/> /// <inheritdoc/>

53
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs

@ -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)
{
}
/// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector128<float> rBase =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector128<float> gBase =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector128<float> bBase =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
// Used for the color conversion
var scale = Vector128.Create(1 / this.MaximumValue);
nuint n = values.Component0.Vector128Count<float>();
for (nuint i = 0; i < n; i++)
{
ref Vector128<float> r = ref Unsafe.Add(ref rBase, i);
ref Vector128<float> g = ref Unsafe.Add(ref gBase, i);
ref Vector128<float> b = ref Unsafe.Add(ref bBase, i);
r = AdvSimd.Multiply(r, scale);
g = AdvSimd.Multiply(g, scale);
b = AdvSimd.Multiply(b, scale);
}
}
/// <inheritdoc/>
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
rLane.CopyTo(values.Component0);
gLane.CopyTo(values.Component1);
bLane.CopyTo(values.Component2);
}
}
}

52
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs

@ -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)
{
}
/// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector256<float> rBase =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> gBase =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> bBase =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
// Used for the color conversion
var scale = Vector256.Create(1 / this.MaximumValue);
nuint n = values.Component0.Vector256Count<float>();
for (nuint i = 0; i < n; i++)
{
ref Vector256<float> r = ref Unsafe.Add(ref rBase, i);
ref Vector256<float> g = ref Unsafe.Add(ref gBase, i);
ref Vector256<float> b = ref Unsafe.Add(ref bBase, i);
r = Avx.Multiply(r, scale);
g = Avx.Multiply(g, scale);
b = Avx.Multiply(b, scale);
}
}
/// <inheritdoc/>
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
rLane.CopyTo(values.Component0);
gLane.CopyTo(values.Component1);
bLane.CopyTo(values.Component2);
}
}
}

2
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs

@ -13,7 +13,7 @@ internal abstract partial class JpegColorConverterBase
} }
/// <inheritdoc/> /// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values) public override void ConvertToRgbInPlace(in ComponentValues values)
=> ConvertToRgbInplace(values, this.MaximumValue); => ConvertToRgbInplace(values, this.MaximumValue);
/// <inheritdoc/> /// <inheritdoc/>

122
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrArm.cs

@ -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)
{
}
/// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector128<float> c0Base =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector128<float> c1Base =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector128<float> c2Base =
ref Unsafe.As<float, Vector128<float>>(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<float>.Count;
for (nuint i = 0; i < n; i++)
{
// y = yVals[i];
// cb = cbVals[i] - 128F;
// cr = crVals[i] - 128F;
ref Vector128<float> c0 = ref Unsafe.Add(ref c0Base, i);
ref Vector128<float> c1 = ref Unsafe.Add(ref c1Base, i);
ref Vector128<float> c2 = ref Unsafe.Add(ref c2Base, i);
Vector128<float> y = c0;
Vector128<float> cb = AdvSimd.Add(c1, chromaOffset);
Vector128<float> cr = AdvSimd.Add(c2, chromaOffset);
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
Vector128<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult);
Vector128<float> g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
Vector128<float> 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;
}
}
/// <inheritdoc/>
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
ref Vector128<float> destY =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector128<float> destCb =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector128<float> destCr =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector128<float> srcR =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(rLane));
ref Vector128<float> srcG =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(gLane));
ref Vector128<float> srcB =
ref Unsafe.As<float, Vector128<float>>(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<float>.Count;
for (nuint i = 0; i < n; i++)
{
Vector128<float> r = Unsafe.Add(ref srcR, i);
Vector128<float> g = Unsafe.Add(ref srcG, i);
Vector128<float> 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<float> y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f0114, b), f0587, g), f0299, r);
Vector128<float> cb = AdvSimd.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f05, b), fn0331264, g), fn0168736, r));
Vector128<float> 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;
}
}
}
}

121
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs

@ -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)
{
}
/// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> c1Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> c2Base =
ref Unsafe.As<float, Vector256<float>>(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<float>();
for (nuint i = 0; i < n; i++)
{
// y = yVals[i];
// cb = cbVals[i] - 128F;
// cr = crVals[i] - 128F;
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i);
ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i);
ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i);
Vector256<float> y = c0;
Vector256<float> cb = Avx.Add(c1, chromaOffset);
Vector256<float> cr = Avx.Add(c2, chromaOffset);
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult);
Vector256<float> g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
Vector256<float> 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;
}
}
/// <inheritdoc/>
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
ref Vector256<float> destY =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> destCb =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> destCr =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector256<float> srcR =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(rLane));
ref Vector256<float> srcG =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(gLane));
ref Vector256<float> srcB =
ref Unsafe.As<float, Vector256<float>>(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<float>();
for (nuint i = 0; i < n; i++)
{
Vector256<float> r = Unsafe.Add(ref srcR, i);
Vector256<float> g = Unsafe.Add(ref srcG, i);
Vector256<float> 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<float> y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r);
Vector256<float> cb = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r));
Vector256<float> 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;
}
}
}
}

2
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrScalar.cs

@ -19,7 +19,7 @@ internal abstract partial class JpegColorConverterBase
} }
/// <inheritdoc/> /// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values) public override void ConvertToRgbInPlace(in ComponentValues values)
=> ConvertToRgbInplace(values, this.MaximumValue, this.HalfValue); => ConvertToRgbInplace(values, this.MaximumValue, this.HalfValue);
/// <inheritdoc/> /// <inheritdoc/>

36
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs

@ -27,13 +27,13 @@ internal abstract partial class JpegColorConverterBase
ref Vector<float> c2Base = ref Vector<float> c2Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
var chromaOffset = new Vector<float>(-this.HalfValue); Vector<float> chromaOffset = new(-this.HalfValue);
var scale = new Vector<float>(1 / this.MaximumValue); Vector<float> scale = new(1 / this.MaximumValue);
var rCrMult = new Vector<float>(YCbCrScalar.RCrMult); Vector<float> rCrMult = new(YCbCrScalar.RCrMult);
var gCbMult = new Vector<float>(-YCbCrScalar.GCbMult); Vector<float> gCbMult = new(-YCbCrScalar.GCbMult);
var gCrMult = new Vector<float>(-YCbCrScalar.GCrMult); Vector<float> gCrMult = new(-YCbCrScalar.GCrMult);
var bCbMult = new Vector<float>(YCbCrScalar.BCbMult); Vector<float> bCbMult = new(YCbCrScalar.BCbMult);
nuint n = values.Component0.VectorCount<float>(); nuint n = values.Component0.VectorCount<float>();
for (nuint i = 0; i < n; i++) for (nuint i = 0; i < n; i++)
@ -89,19 +89,19 @@ internal abstract partial class JpegColorConverterBase
ref Vector<float> srcB = ref Vector<float> srcB =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(bLane)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(bLane));
var chromaOffset = new Vector<float>(this.HalfValue); Vector<float> chromaOffset = new(this.HalfValue);
var rYMult = new Vector<float>(0.299f); Vector<float> rYMult = new(0.299f);
var gYMult = new Vector<float>(0.587f); Vector<float> gYMult = new(0.587f);
var bYMult = new Vector<float>(0.114f); Vector<float> bYMult = new(0.114f);
var rCbMult = new Vector<float>(0.168736f); Vector<float> rCbMult = new(0.168736f);
var gCbMult = new Vector<float>(0.331264f); Vector<float> gCbMult = new(0.331264f);
var bCbMult = new Vector<float>(0.5f); Vector<float> bCbMult = new(0.5f);
var rCrMult = new Vector<float>(0.5f); Vector<float> rCrMult = new(0.5f);
var gCrMult = new Vector<float>(0.418688f); Vector<float> gCrMult = new(0.418688f);
var bCrMult = new Vector<float>(0.081312f); Vector<float> bCrMult = new(0.081312f);
nuint n = values.Component0.VectorCount<float>(); nuint n = values.Component0.VectorCount<float>();
for (nuint i = 0; i < n; i++) for (nuint i = 0; i < n; i++)
@ -120,7 +120,7 @@ internal abstract partial class JpegColorConverterBase
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
=> YCbCrScalar.ConvertFromRgb(values, this.HalfValue, r, g, b); => YCbCrScalar.ConvertFromRgb(values, this.HalfValue, rLane, gLane, bLane);
} }
} }

133
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKArm64.cs

@ -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)
{
}
/// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector128<float> c0Base =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector128<float> c1Base =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector128<float> c2Base =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector128<float> kBase =
ref Unsafe.As<float, Vector128<float>>(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<float>.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<float> c0 = ref Unsafe.Add(ref c0Base, i);
ref Vector128<float> c1 = ref Unsafe.Add(ref c1Base, i);
ref Vector128<float> c2 = ref Unsafe.Add(ref c2Base, i);
Vector128<float> y = c0;
Vector128<float> cb = AdvSimd.Add(c1, chromaOffset);
Vector128<float> cr = AdvSimd.Add(c2, chromaOffset);
Vector128<float> 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<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult);
Vector128<float> g =
HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
Vector128<float> 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;
}
}
/// <inheritdoc/>
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
// rgb -> cmyk
CmykArm64.ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane);
// cmyk -> ycck
ref Vector128<float> destY =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector128<float> destCb =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector128<float> destCr =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector128<float> srcR = ref destY;
ref Vector128<float> srcG = ref destCb;
ref Vector128<float> 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<float>.Count;
for (nuint i = 0; i < n; i++)
{
Vector128<float> r = AdvSimd.Subtract(maxSampleValue, Unsafe.Add(ref srcR, i));
Vector128<float> g = AdvSimd.Subtract(maxSampleValue, Unsafe.Add(ref srcG, i));
Vector128<float> 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<float> y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f0114, b), f0587, g), f0299, r);
Vector128<float> cb = AdvSimd.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f05, b), fn0331264, g), fn0168736, r));
Vector128<float> 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;
}
}
}
}

132
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs

@ -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)
{
}
/// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> c1Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> c2Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector256<float> kBase =
ref Unsafe.As<float, Vector256<float>>(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<float>();
for (nuint i = 0; i < n; i++)
{
// y = yVals[i];
// cb = cbVals[i] - 128F;
// cr = crVals[i] - 128F;
// k = kVals[i] / 256F;
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i);
ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i);
ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i);
Vector256<float> y = c0;
Vector256<float> cb = Avx.Add(c1, chromaOffset);
Vector256<float> cr = Avx.Add(c2, chromaOffset);
Vector256<float> 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<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult);
Vector256<float> g =
HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
Vector256<float> 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;
}
}
/// <inheritdoc/>
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
// rgb -> cmyk
CmykAvx.ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane);
// cmyk -> ycck
ref Vector256<float> destY =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> destCb =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> destCr =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector256<float> srcR = ref destY;
ref Vector256<float> srcG = ref destCb;
ref Vector256<float> 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<float>();
for (nuint i = 0; i < n; i++)
{
Vector256<float> r = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcR, i));
Vector256<float> g = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcG, i));
Vector256<float> 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<float> y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r);
Vector256<float> cb = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r));
Vector256<float> 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;
}
}
}
}

2
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKScalar.cs

@ -19,7 +19,7 @@ internal abstract partial class JpegColorConverterBase
} }
/// <inheritdoc/> /// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values) public override void ConvertToRgbInPlace(in ComponentValues values)
=> ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue); => ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue);
/// <inheritdoc/> /// <inheritdoc/>

35
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs

@ -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
{
/// <summary>
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
/// based on <see cref="Avx"/> instructions.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
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<float>.Count;
}
}

35
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm64.cs

@ -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
{
/// <summary>
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
/// based on <see cref="Avx"/> instructions.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
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<float>.Count;
}
}

34
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterAvx.cs

@ -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
{
/// <summary>
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
/// based on <see cref="Avx"/> instructions.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
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<float>.Count;
}
}

123
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs

@ -71,25 +71,14 @@ internal abstract partial class JpegColorConverterBase
/// <param name="precision">The precision in bits.</param> /// <param name="precision">The precision in bits.</param>
/// <exception cref="InvalidImageContentException">Invalid colorspace.</exception> /// <exception cref="InvalidImageContentException">Invalid colorspace.</exception>
public static JpegColorConverterBase GetConverter(JpegColorSpace colorSpace, int precision) public static JpegColorConverterBase GetConverter(JpegColorSpace colorSpace, int precision)
{ => Array.Find(Converters, c => c.ColorSpace == colorSpace && c.Precision == precision)
JpegColorConverterBase converter = Array.Find( ?? throw new InvalidImageContentException($"Could not find any converter for JpegColorSpace {colorSpace}!");
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;
}
/// <summary> /// <summary>
/// Converts planar jpeg component values in <paramref name="values"/> to RGB color space inplace. /// Converts planar jpeg component values in <paramref name="values"/> to RGB color space in-place.
/// </summary> /// </summary>
/// <param name="values">The input/ouptut as a stack-only <see cref="ComponentValues"/> struct</param> /// <param name="values">The input/output as a stack-only <see cref="ComponentValues"/> struct</param>
public abstract void ConvertToRgbInplace(in ComponentValues values); public abstract void ConvertToRgbInPlace(in ComponentValues values);
/// <summary> /// <summary>
/// Converts RGB lanes to jpeg component 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<float> rLane, Span<float> gLane, Span<float> bLane); public abstract void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane);
/// <summary> /// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for all supported colorspaces and precisions. /// Returns the <see cref="JpegColorConverterBase"/>s for all supported color spaces and precisions.
/// </summary> /// </summary>
private static JpegColorConverterBase[] CreateConverters() private static JpegColorConverterBase[] CreateConverters()
{ => [
// 5 color types with 2 supported precisions: 8 bit & 12 bit
const int colorConvertersCount = 5 * 2; // 8-bit converters
GetYCbCrConverter(8),
JpegColorConverterBase[] converters = new JpegColorConverterBase[colorConvertersCount]; GetYccKConverter(8),
GetCmykConverter(8),
// 8-bit converters GetGrayScaleConverter(8),
converters[0] = GetYCbCrConverter(8); GetRgbConverter(8),
converters[1] = GetYccKConverter(8);
converters[2] = GetCmykConverter(8); // 12-bit converters
converters[3] = GetGrayScaleConverter(8); GetYCbCrConverter(12),
converters[4] = GetRgbConverter(8); GetYccKConverter(12),
GetCmykConverter(12),
// 12-bit converters GetGrayScaleConverter(12),
converters[5] = GetYCbCrConverter(12); GetRgbConverter(12),
converters[6] = GetYccKConverter(12); ];
converters[7] = GetCmykConverter(12);
converters[8] = GetGrayScaleConverter(12);
converters[9] = GetRgbConverter(12);
return converters;
}
/// <summary> /// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for the YCbCr colorspace. /// Returns the <see cref="JpegColorConverterBase"/>s for the YCbCr colorspace.
@ -133,16 +116,6 @@ internal abstract partial class JpegColorConverterBase
/// <param name="precision">The precision in bits.</param> /// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetYCbCrConverter(int precision) private static JpegColorConverterBase GetYCbCrConverter(int precision)
{ {
if (JpegColorConverterAvx.IsSupported)
{
return new YCbCrAvx(precision);
}
if (JpegColorConverterArm.IsSupported)
{
return new YCbCrArm(precision);
}
if (JpegColorConverterVector.IsSupported) if (JpegColorConverterVector.IsSupported)
{ {
return new YCbCrVector(precision); return new YCbCrVector(precision);
@ -157,16 +130,6 @@ internal abstract partial class JpegColorConverterBase
/// <param name="precision">The precision in bits.</param> /// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetYccKConverter(int precision) private static JpegColorConverterBase GetYccKConverter(int precision)
{ {
if (JpegColorConverterAvx.IsSupported)
{
return new YccKAvx(precision);
}
if (JpegColorConverterArm64.IsSupported)
{
return new YccKArm64(precision);
}
if (JpegColorConverterVector.IsSupported) if (JpegColorConverterVector.IsSupported)
{ {
return new YccKVector(precision); return new YccKVector(precision);
@ -181,16 +144,6 @@ internal abstract partial class JpegColorConverterBase
/// <param name="precision">The precision in bits.</param> /// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetCmykConverter(int precision) private static JpegColorConverterBase GetCmykConverter(int precision)
{ {
if (JpegColorConverterAvx.IsSupported)
{
return new CmykAvx(precision);
}
if (JpegColorConverterArm64.IsSupported)
{
return new CmykArm64(precision);
}
if (JpegColorConverterVector.IsSupported) if (JpegColorConverterVector.IsSupported)
{ {
return new CmykVector(precision); return new CmykVector(precision);
@ -205,16 +158,6 @@ internal abstract partial class JpegColorConverterBase
/// <param name="precision">The precision in bits.</param> /// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetGrayScaleConverter(int precision) private static JpegColorConverterBase GetGrayScaleConverter(int precision)
{ {
if (JpegColorConverterAvx.IsSupported)
{
return new GrayscaleAvx(precision);
}
if (JpegColorConverterArm.IsSupported)
{
return new GrayscaleArm(precision);
}
if (JpegColorConverterVector.IsSupported) if (JpegColorConverterVector.IsSupported)
{ {
return new GrayScaleVector(precision); return new GrayScaleVector(precision);
@ -229,16 +172,6 @@ internal abstract partial class JpegColorConverterBase
/// <param name="precision">The precision in bits.</param> /// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetRgbConverter(int precision) private static JpegColorConverterBase GetRgbConverter(int precision)
{ {
if (JpegColorConverterAvx.IsSupported)
{
return new RgbAvx(precision);
}
if (JpegColorConverterArm.IsSupported)
{
return new RgbArm(precision);
}
if (JpegColorConverterVector.IsSupported) if (JpegColorConverterVector.IsSupported)
{ {
return new RgbVector(precision); 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 // In case of grayscale, Component1 and Component2 point to Component0 memory area
this.Component1 = this.ComponentCount > 1 ? componentBuffers[1].DangerousGetRowSpan(row) : this.Component0; this.Component1 = this.ComponentCount > 1 ? componentBuffers[1].DangerousGetRowSpan(row) : this.Component0;
this.Component2 = this.ComponentCount > 2 ? componentBuffers[2].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<float>.Empty; this.Component3 = this.ComponentCount > 3 ? componentBuffers[3].DangerousGetRowSpan(row) : [];
} }
/// <summary> /// <summary>
@ -314,7 +247,7 @@ internal abstract partial class JpegColorConverterBase
// In case of grayscale, Component1 and Component2 point to Component0 memory area // In case of grayscale, Component1 and Component2 point to Component0 memory area
this.Component1 = this.ComponentCount > 1 ? processors[1].GetColorBufferRowSpan(row) : this.Component0; this.Component1 = this.ComponentCount > 1 ? processors[1].GetColorBufferRowSpan(row) : this.Component0;
this.Component2 = this.ComponentCount > 2 ? processors[2].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<float>.Empty; this.Component3 = this.ComponentCount > 3 ? processors[3].GetColorBufferRowSpan(row) : [];
} }
/// <summary> /// <summary>
@ -333,7 +266,7 @@ internal abstract partial class JpegColorConverterBase
// In case of grayscale, Component1 and Component2 point to Component0 memory area // In case of grayscale, Component1 and Component2 point to Component0 memory area
this.Component1 = this.ComponentCount > 1 ? processors[1].GetColorBufferRowSpan(row) : this.Component0; this.Component1 = this.ComponentCount > 1 ? processors[1].GetColorBufferRowSpan(row) : this.Component0;
this.Component2 = this.ComponentCount > 2 ? processors[2].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<float>.Empty; this.Component3 = this.ComponentCount > 3 ? processors[3].GetColorBufferRowSpan(row) : [];
} }
internal ComponentValues( internal ComponentValues(
@ -353,9 +286,9 @@ internal abstract partial class JpegColorConverterBase
public ComponentValues Slice(int start, int length) public ComponentValues Slice(int start, int length)
{ {
Span<float> c0 = this.Component0.Slice(start, length); Span<float> c0 = this.Component0.Slice(start, length);
Span<float> c1 = this.Component1.Length > 0 ? this.Component1.Slice(start, length) : Span<float>.Empty; Span<float> c1 = this.Component1.Length > 0 ? this.Component1.Slice(start, length) : [];
Span<float> c2 = this.Component2.Length > 0 ? this.Component2.Slice(start, length) : Span<float>.Empty; Span<float> c2 = this.Component2.Length > 0 ? this.Component2.Slice(start, length) : [];
Span<float> c3 = this.Component3.Length > 0 ? this.Component3.Slice(start, length) : Span<float>.Empty; Span<float> c3 = this.Component3.Length > 0 ? this.Component3.Slice(start, length) : [];
return new ComponentValues(this.ComponentCount, c0, c1, c2, c3); return new ComponentValues(this.ComponentCount, c0, c1, c2, c3);
} }

2
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterVector.cs

@ -36,7 +36,7 @@ internal abstract partial class JpegColorConverterBase
public override int ElementsPerBatch => Vector<float>.Count; public override int ElementsPerBatch => Vector<float>.Count;
/// <inheritdoc/> /// <inheritdoc/>
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."); DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware.");

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs

@ -141,7 +141,7 @@ internal class SpectralConverter<TPixel> : SpectralConverter, IDisposable
JpegColorConverterBase.ComponentValues values = new(this.componentProcessors, y); JpegColorConverterBase.ComponentValues values = new(this.componentProcessors, y);
this.colorConverter.ConvertToRgbInplace(values); this.colorConverter.ConvertToRgbInPlace(values);
values = values.Slice(0, width); // slice away Jpeg padding values = values.Slice(0, width); // slice away Jpeg padding
Span<byte> r = this.rgbBuffer.Slice(0, width); Span<byte> r = this.rgbBuffer.Slice(0, width);

20
tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs

@ -19,7 +19,7 @@ public class CmykColorConversion : ColorConversionBenchmark
{ {
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverterBase.CmykScalar(8).ConvertToRgbInplace(values); new JpegColorConverterBase.CmykScalar(8).ConvertToRgbInPlace(values);
} }
[Benchmark] [Benchmark]
@ -27,22 +27,6 @@ public class CmykColorConversion : ColorConversionBenchmark
{ {
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverterBase.CmykVector(8).ConvertToRgbInplace(values); 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);
} }
} }

14
tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs

@ -19,22 +19,14 @@ public class GrayscaleColorConversion : ColorConversionBenchmark
{ {
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverterBase.GrayscaleScalar(8).ConvertToRgbInplace(values); new JpegColorConverterBase.GrayscaleScalar(8).ConvertToRgbInPlace(values);
} }
[Benchmark] [Benchmark]
public void SimdVectorAvx() public void SimdVector8()
{ {
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverterBase.GrayscaleAvx(8).ConvertToRgbInplace(values); new JpegColorConverterBase.GrayScaleVector(8).ConvertToRgbInPlace(values);
}
[Benchmark]
public void SimdVectorArm()
{
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverterBase.GrayscaleArm(8).ConvertToRgbInplace(values);
} }
} }

20
tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs

@ -19,7 +19,7 @@ public class RgbColorConversion : ColorConversionBenchmark
{ {
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverterBase.RgbScalar(8).ConvertToRgbInplace(values); new JpegColorConverterBase.RgbScalar(8).ConvertToRgbInPlace(values);
} }
[Benchmark] [Benchmark]
@ -27,22 +27,6 @@ public class RgbColorConversion : ColorConversionBenchmark
{ {
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverterBase.RgbVector(8).ConvertToRgbInplace(values); 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);
} }
} }

20
tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs

@ -19,7 +19,7 @@ public class YCbCrColorConversion : ColorConversionBenchmark
{ {
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverterBase.YCbCrScalar(8).ConvertToRgbInplace(values); new JpegColorConverterBase.YCbCrScalar(8).ConvertToRgbInPlace(values);
} }
[Benchmark] [Benchmark]
@ -27,22 +27,6 @@ public class YCbCrColorConversion : ColorConversionBenchmark
{ {
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverterBase.YCbCrVector(8).ConvertToRgbInplace(values); 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);
} }
} }

20
tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs

@ -19,7 +19,7 @@ public class YccKColorConverter : ColorConversionBenchmark
{ {
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverterBase.YccKScalar(8).ConvertToRgbInplace(values); new JpegColorConverterBase.YccKScalar(8).ConvertToRgbInPlace(values);
} }
[Benchmark] [Benchmark]
@ -27,22 +27,6 @@ public class YccKColorConverter : ColorConversionBenchmark
{ {
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverterBase.YccKVector(8).ConvertToRgbInplace(values); 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);
} }
} }

218
tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs

@ -83,7 +83,7 @@ public class JpegColorConverterTests
Type expectedType = typeof(JpegColorConverterBase.RgbScalar); Type expectedType = typeof(JpegColorConverterBase.RgbScalar);
if (Avx.IsSupported) if (Avx.IsSupported)
{ {
expectedType = typeof(JpegColorConverterBase.RgbAvx); expectedType = typeof(JpegColorConverterBase.RgbVector);
} }
else if (Sse2.IsSupported) else if (Sse2.IsSupported)
{ {
@ -91,7 +91,7 @@ public class JpegColorConverterTests
} }
else if (AdvSimd.IsSupported) else if (AdvSimd.IsSupported)
{ {
expectedType = typeof(JpegColorConverterBase.RgbArm); expectedType = typeof(JpegColorConverterBase.RgbVector);
} }
// act // act
@ -116,7 +116,7 @@ public class JpegColorConverterTests
Type expectedType = typeof(JpegColorConverterBase.GrayscaleScalar); Type expectedType = typeof(JpegColorConverterBase.GrayscaleScalar);
if (Avx.IsSupported) if (Avx.IsSupported)
{ {
expectedType = typeof(JpegColorConverterBase.GrayscaleAvx); expectedType = typeof(JpegColorConverterBase.GrayScaleVector);
} }
else if (Sse2.IsSupported) else if (Sse2.IsSupported)
{ {
@ -124,7 +124,7 @@ public class JpegColorConverterTests
} }
else if (AdvSimd.IsSupported) else if (AdvSimd.IsSupported)
{ {
expectedType = typeof(JpegColorConverterBase.GrayscaleArm); expectedType = typeof(JpegColorConverterBase.GrayScaleVector);
} }
// act // act
@ -149,7 +149,7 @@ public class JpegColorConverterTests
Type expectedType = typeof(JpegColorConverterBase.CmykScalar); Type expectedType = typeof(JpegColorConverterBase.CmykScalar);
if (Avx.IsSupported) if (Avx.IsSupported)
{ {
expectedType = typeof(JpegColorConverterBase.CmykAvx); expectedType = typeof(JpegColorConverterBase.CmykVector);
} }
else if (Sse2.IsSupported) else if (Sse2.IsSupported)
{ {
@ -157,7 +157,7 @@ public class JpegColorConverterTests
} }
else if (AdvSimd.Arm64.IsSupported) else if (AdvSimd.Arm64.IsSupported)
{ {
expectedType = typeof(JpegColorConverterBase.CmykArm64); expectedType = typeof(JpegColorConverterBase.CmykVector);
} }
// act // act
@ -182,7 +182,7 @@ public class JpegColorConverterTests
Type expectedType = typeof(JpegColorConverterBase.YCbCrScalar); Type expectedType = typeof(JpegColorConverterBase.YCbCrScalar);
if (Avx.IsSupported) if (Avx.IsSupported)
{ {
expectedType = typeof(JpegColorConverterBase.YCbCrAvx); expectedType = typeof(JpegColorConverterBase.YCbCrVector);
} }
else if (Sse2.IsSupported) else if (Sse2.IsSupported)
{ {
@ -190,7 +190,7 @@ public class JpegColorConverterTests
} }
else if (AdvSimd.IsSupported) else if (AdvSimd.IsSupported)
{ {
expectedType = typeof(JpegColorConverterBase.YCbCrArm); expectedType = typeof(JpegColorConverterBase.YCbCrVector);
} }
// act // act
@ -215,7 +215,7 @@ public class JpegColorConverterTests
Type expectedType = typeof(JpegColorConverterBase.YccKScalar); Type expectedType = typeof(JpegColorConverterBase.YccKScalar);
if (Avx.IsSupported) if (Avx.IsSupported)
{ {
expectedType = typeof(JpegColorConverterBase.YccKAvx); expectedType = typeof(JpegColorConverterBase.YccKVector);
} }
else if (Sse2.IsSupported) else if (Sse2.IsSupported)
{ {
@ -223,7 +223,7 @@ public class JpegColorConverterTests
} }
else if (AdvSimd.Arm64.IsSupported) else if (AdvSimd.Arm64.IsSupported)
{ {
expectedType = typeof(JpegColorConverterBase.YccKArm64); expectedType = typeof(JpegColorConverterBase.YccKVector);
} }
// act // act
@ -405,176 +405,6 @@ public class JpegColorConverterTests
new JpegColorConverterBase.YccKScalar(8)); 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( private void TestConversionToRgb(
JpegColorConverterBase converter, JpegColorConverterBase converter,
int componentCount, int componentCount,
@ -595,28 +425,6 @@ public class JpegColorConverterTests
baseLineConverter); 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( private static JpegColorConverterBase.ComponentValues CreateRandomValues(
int length, int length,
int componentCount, int componentCount,
@ -669,7 +477,7 @@ public class JpegColorConverterTests
original.Component2.ToArray(), original.Component2.ToArray(),
original.Component3.ToArray()); original.Component3.ToArray());
converter.ConvertToRgbInplace(actual); converter.ConvertToRgbInPlace(actual);
for (int i = 0; i < TestBufferLength; i++) for (int i = 0; i < TestBufferLength; i++)
{ {
@ -685,7 +493,7 @@ public class JpegColorConverterTests
original.Component1.ToArray(), original.Component1.ToArray(),
original.Component2.ToArray(), original.Component2.ToArray(),
original.Component3.ToArray()); original.Component3.ToArray());
baseLineConverter.ConvertToRgbInplace(expected); baseLineConverter.ConvertToRgbInPlace(expected);
if (componentCount == 1) if (componentCount == 1)
{ {
Assert.True(expected.Component0.SequenceEqual(actual.Component0)); Assert.True(expected.Component0.SequenceEqual(actual.Component0));
@ -781,7 +589,7 @@ public class JpegColorConverterTests
ValidateYCbCr(original, result, i); ValidateYCbCr(original, result, i);
break; break;
default: default:
Assert.True(false, $"Invalid Colorspace enum value: {colorSpace}."); Assert.Fail($"Invalid Colorspace enum value: {colorSpace}.");
break; break;
} }
} }

Loading…
Cancel
Save