Browse Source

Port RGB converter

pull/2917/head
James Jackson-South 9 months ago
parent
commit
57614df1c0
  1. 11
      src/ImageSharp/Common/Helpers/SimdUtils.cs
  2. 4
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs
  3. 104
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs
  4. 2
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs
  5. 72
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs
  6. 59
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs
  7. 51
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector128.cs
  8. 51
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector256.cs
  9. 59
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector512.cs
  10. 136
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs
  11. 14
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs
  12. 24
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs
  13. 8
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs
  14. 85
      tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs

11
src/ImageSharp/Common/Helpers/SimdUtils.cs

@ -40,17 +40,6 @@ internal static partial class SimdUtils
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector<float> RoundToNearestInteger(this Vector<float> v)
{
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);

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

@ -14,13 +14,13 @@ internal abstract partial class JpegColorConverterBase
/// <inheritdoc/>
public override void ConvertToRgbInPlace(in ComponentValues values) =>
ConvertToRgbInplace(values, this.MaximumValue);
ConvertToRgbInPlace(values, this.MaximumValue);
/// <inheritdoc/>
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> 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<float> c0 = values.Component0;
Span<float> c1 = values.Component1;

104
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs

@ -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)
{
}
/// <inheritdoc/>
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values)
{
ref Vector<float> cBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> mBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> yBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector<float> kBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3));
var scale = new Vector<float>(1 / (this.MaximumValue * this.MaximumValue));
nuint n = values.Component0.VectorCount<float>();
for (nuint i = 0; i < n; i++)
{
ref Vector<float> c = ref Unsafe.Add(ref cBase, i);
ref Vector<float> m = ref Unsafe.Add(ref mBase, i);
ref Vector<float> y = ref Unsafe.Add(ref yBase, i);
Vector<float> k = Unsafe.Add(ref kBase, i);
k *= scale;
c *= k;
m *= k;
y *= k;
}
}
/// <inheritdoc/>
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values)
=> CmykScalar.ConvertToRgbInplace(values, this.MaximumValue);
/// <inheritdoc/>
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
=> ConvertFromRgbInplaceVectorized(in values, this.MaximumValue, r, g, b);
/// <inheritdoc/>
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
=> ConvertFromRgbInplaceRemainder(values, this.MaximumValue, r, g, b);
public static void ConvertFromRgbInplaceVectorized(in ComponentValues values, float maxValue, Span<float> r, Span<float> g, Span<float> b)
{
ref Vector<float> destC =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> destM =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> destY =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector<float> destK =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3));
ref Vector<float> srcR =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(r));
ref Vector<float> srcG =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(g));
ref Vector<float> srcB =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(b));
// Used for the color conversion
var scale = new Vector<float>(maxValue);
nuint n = values.Component0.VectorCount<float>();
for (nuint i = 0; i < n; i++)
{
Vector<float> ctmp = scale - Unsafe.Add(ref srcR, i);
Vector<float> mtmp = scale - Unsafe.Add(ref srcG, i);
Vector<float> ytmp = scale - Unsafe.Add(ref srcB, i);
Vector<float> ktmp = Vector.Min(ctmp, Vector.Min(mtmp, ytmp));
var kMask = Vector.Equals(ktmp, scale);
ctmp = Vector.AndNot((ctmp - ktmp) / (scale - ktmp), kMask.As<int, float>());
mtmp = Vector.AndNot((mtmp - ktmp) / (scale - ktmp), kMask.As<int, float>());
ytmp = Vector.AndNot((ytmp - ktmp) / (scale - ktmp), kMask.As<int, float>());
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<float> r, Span<float> g, Span<float> b)
=> CmykScalar.ConvertFromRgb(values, maxValue, r, g, b);
}
}

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

@ -52,7 +52,7 @@ internal abstract partial class JpegColorConverterBase
/// <inheritdoc/>
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values)
=> CmykScalar.ConvertToRgbInplace(values, this.MaximumValue);
=> CmykScalar.ConvertToRgbInPlace(values, this.MaximumValue);
/// <inheritdoc/>
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)

72
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs

@ -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)
{
}
/// <inheritdoc/>
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values)
{
ref Vector<float> cBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
var scale = new Vector<float>(1 / this.MaximumValue);
nuint n = values.Component0.VectorCount<float>();
for (nuint i = 0; i < n; i++)
{
ref Vector<float> c0 = ref Unsafe.Add(ref cBase, i);
c0 *= scale;
}
}
/// <inheritdoc/>
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values)
=> GrayScaleScalar.ConvertToRgbInPlace(values.Component0, this.MaximumValue);
/// <inheritdoc/>
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
ref Vector<float> destLuma =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> srcR =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(rLane));
ref Vector<float> srcG =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(gLane));
ref Vector<float> srcB =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(bLane));
var rMult = new Vector<float>(0.299f);
var gMult = new Vector<float>(0.587f);
var bMult = new Vector<float>(0.114f);
nuint n = values.Component0.VectorCount<float>();
for (nuint i = 0; i < n; i++)
{
Vector<float> r = Unsafe.Add(ref srcR, i);
Vector<float> g = Unsafe.Add(ref srcR, i);
Vector<float> 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);
}
}
/// <inheritdoc/>
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
=> GrayScaleScalar.ConvertFromRgbScalar(values, r, g, b);
}
}

59
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs

@ -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)
{
}
/// <inheritdoc/>
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values)
{
ref Vector<float> rBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> gBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> bBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
var scale = new Vector<float>(1 / this.MaximumValue);
nuint n = values.Component0.VectorCount<float>();
for (nuint i = 0; i < n; i++)
{
ref Vector<float> r = ref Unsafe.Add(ref rBase, i);
ref Vector<float> g = ref Unsafe.Add(ref gBase, i);
ref Vector<float> b = ref Unsafe.Add(ref bBase, i);
r *= scale;
g *= scale;
b *= scale;
}
}
/// <inheritdoc/>
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values)
=> RgbScalar.ConvertToRgbInPlace(values, this.MaximumValue);
/// <inheritdoc/>
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
{
r.CopyTo(values.Component0);
g.CopyTo(values.Component1);
b.CopyTo(values.Component2);
}
/// <inheritdoc/>
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
=> RgbScalar.ConvertFromRgb(values, r, g, b);
}
}

51
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)
{
}
/// <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
Vector128<float> 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 *= scale;
g *= scale;
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);
}
}
}

51
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)
{
}
/// <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
Vector256<float> 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 *= scale;
g *= scale;
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);
}
}
}

59
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)
{
}
/// <inheritdoc/>
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values)
{
ref Vector512<float> rBase =
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector512<float> gBase =
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector512<float> bBase =
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component2));
// Used for the color conversion
Vector512<float> scale = Vector512.Create(1 / this.MaximumValue);
nuint n = values.Component0.Vector512Count<float>();
for (nuint i = 0; i < n; i++)
{
ref Vector512<float> r = ref Unsafe.Add(ref rBase, i);
ref Vector512<float> g = ref Unsafe.Add(ref gBase, i);
ref Vector512<float> b = ref Unsafe.Add(ref bBase, i);
r *= scale;
g *= scale;
b *= scale;
}
}
/// <inheritdoc/>
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
rLane.CopyTo(values.Component0);
gLane.CopyTo(values.Component1);
bLane.CopyTo(values.Component2);
}
/// <inheritdoc/>
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values)
=> RgbScalar.ConvertToRgbInPlace(values, this.MaximumValue);
/// <inheritdoc/>
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
=> RgbScalar.ConvertFromRgb(values, rLane, gLane, bLane);
}
}

136
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs

@ -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)
{
}
/// <inheritdoc/>
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values)
{
ref Vector<float> c0Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> c1Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> c2Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector<float> kBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3));
var chromaOffset = new Vector<float>(-this.HalfValue);
var scale = new Vector<float>(1 / (this.MaximumValue * this.MaximumValue));
var max = new Vector<float>(this.MaximumValue);
var rCrMult = new Vector<float>(YCbCrScalar.RCrMult);
var gCbMult = new Vector<float>(-YCbCrScalar.GCbMult);
var gCrMult = new Vector<float>(-YCbCrScalar.GCrMult);
var bCbMult = new Vector<float>(YCbCrScalar.BCbMult);
nuint n = values.Component0.VectorCount<float>();
for (nuint i = 0; i < n; i++)
{
// y = yVals[i];
// cb = cbVals[i] - 128F;
// cr = crVals[i] - 128F;
// k = kVals[i] / 256F;
ref Vector<float> c0 = ref Unsafe.Add(ref c0Base, i);
ref Vector<float> c1 = ref Unsafe.Add(ref c1Base, i);
ref Vector<float> c2 = ref Unsafe.Add(ref c2Base, i);
Vector<float> y = c0;
Vector<float> cb = c1 + chromaOffset;
Vector<float> cr = c2 + chromaOffset;
Vector<float> 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<float> r = y + (cr * rCrMult);
Vector<float> g = y + (cb * gCbMult) + (cr * gCrMult);
Vector<float> 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;
}
}
/// <inheritdoc/>
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values)
=> YccKScalar.ConvertToRgpInPlace(values, this.MaximumValue, this.HalfValue);
/// <inheritdoc/>
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
// rgb -> cmyk
CmykVector.ConvertFromRgbInplaceVectorized(in values, this.MaximumValue, rLane, gLane, bLane);
// cmyk -> ycck
ref Vector<float> destY =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> destCb =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> destCr =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector<float> srcR = ref destY;
ref Vector<float> srcG = ref destCb;
ref Vector<float> srcB = ref destCr;
var maxSampleValue = new Vector<float>(this.MaximumValue);
var chromaOffset = new Vector<float>(this.HalfValue);
var rYMult = new Vector<float>(0.299f);
var gYMult = new Vector<float>(0.587f);
var bYMult = new Vector<float>(0.114f);
var rCbMult = new Vector<float>(0.168736f);
var gCbMult = new Vector<float>(0.331264f);
var bCbMult = new Vector<float>(0.5f);
var rCrMult = new Vector<float>(0.5f);
var gCrMult = new Vector<float>(0.418688f);
var bCrMult = new Vector<float>(0.081312f);
nuint n = values.Component0.VectorCount<float>();
for (nuint i = 0; i < n; i++)
{
Vector<float> r = maxSampleValue - Unsafe.Add(ref srcR, i);
Vector<float> g = maxSampleValue - Unsafe.Add(ref srcG, i);
Vector<float> 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);
}
}
/// <inheritdoc/>
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> r, Span<float> g, Span<float> 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);
}
}
}

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

@ -212,9 +212,19 @@ internal abstract partial class JpegColorConverterBase
/// <param name="precision">The precision in bits.</param>
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);

24
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);
}
}

8
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()
{

85
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<int>(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))]

Loading…
Cancel
Save