mirror of https://github.com/SixLabors/ImageSharp
58 changed files with 827 additions and 6785 deletions
@ -1,116 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Buffers; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using SixLabors.ImageSharp.ColorProfiles; |
|
||||
using SixLabors.ImageSharp.ColorProfiles.Icc; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class CmykScalar : JpegColorConverterScalar |
|
||||
{ |
|
||||
public CmykScalar(int precision) |
|
||||
: base(JpegColorSpace.Cmyk, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlace(in ComponentValues values) => |
|
||||
ConvertToRgbInPlace(values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> ConvertToRgbInPlaceWithIcc(configuration, profile, 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) |
|
||||
{ |
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
Span<float> c3 = values.Component3; |
|
||||
|
|
||||
float scale = 1 / (maxValue * maxValue); |
|
||||
for (int i = 0; i < c0.Length; i++) |
|
||||
{ |
|
||||
float c = c0[i]; |
|
||||
float m = c1[i]; |
|
||||
float y = c2[i]; |
|
||||
float k = c3[i]; |
|
||||
|
|
||||
k *= scale; |
|
||||
c0[i] = c * k; |
|
||||
c1[i] = m * k; |
|
||||
c2[i] = y * k; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
Span<float> c = values.Component0; |
|
||||
Span<float> m = values.Component1; |
|
||||
Span<float> y = values.Component2; |
|
||||
Span<float> k = values.Component3; |
|
||||
|
|
||||
for (int i = 0; i < c.Length; i++) |
|
||||
{ |
|
||||
float ctmp = 255f - rLane[i]; |
|
||||
float mtmp = 255f - gLane[i]; |
|
||||
float ytmp = 255f - bLane[i]; |
|
||||
float ktmp = MathF.Min(MathF.Min(ctmp, mtmp), ytmp); |
|
||||
|
|
||||
if (ktmp >= 255f) |
|
||||
{ |
|
||||
ctmp = 0f; |
|
||||
mtmp = 0f; |
|
||||
ytmp = 0f; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
ctmp = (ctmp - ktmp) / (255f - ktmp); |
|
||||
mtmp = (mtmp - ktmp) / (255f - ktmp); |
|
||||
ytmp = (ytmp - ktmp) / (255f - ktmp); |
|
||||
} |
|
||||
|
|
||||
c[i] = maxValue - (ctmp * maxValue); |
|
||||
m[i] = maxValue - (mtmp * maxValue); |
|
||||
y[i] = maxValue - (ytmp * maxValue); |
|
||||
k[i] = maxValue - ktmp; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static void ConvertToRgbInPlaceWithIcc(Configuration configuration, IccProfile profile, in ComponentValues values, float maxValue) |
|
||||
{ |
|
||||
using IMemoryOwner<float> memoryOwner = configuration.MemoryAllocator.Allocate<float>(values.Component0.Length * 4); |
|
||||
Span<float> packed = memoryOwner.Memory.Span; |
|
||||
|
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
Span<float> c3 = values.Component3; |
|
||||
|
|
||||
PackedInvertNormalizeInterleave4(c0, c1, c2, c3, packed, maxValue); |
|
||||
|
|
||||
Span<Cmyk> source = MemoryMarshal.Cast<float, Cmyk>(packed); |
|
||||
Span<Rgb> destination = MemoryMarshal.Cast<float, Rgb>(packed)[..source.Length]; |
|
||||
|
|
||||
ColorConversionOptions options = new() |
|
||||
{ |
|
||||
SourceIccProfile = profile, |
|
||||
TargetIccProfile = CompactSrgbV4Profile.Profile, |
|
||||
}; |
|
||||
ColorProfileConverter converter = new(options); |
|
||||
converter.Convert<Cmyk, Rgb>(source, destination); |
|
||||
|
|
||||
UnpackDeinterleave3(MemoryMarshal.Cast<float, Vector3>(packed)[..source.Length], c0, c1, c2); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,100 +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 SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class CmykVector128 : JpegColorConverterVector128 |
|
||||
{ |
|
||||
public CmykVector128(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
|
|
||||
Vector128<float> scale = Vector128.Create(1 / (this.MaximumValue * this.MaximumValue)); |
|
||||
|
|
||||
nuint n = values.Component0.Vector128Count<float>(); |
|
||||
for (nuint 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 *= scale; |
|
||||
c *= k; |
|
||||
m *= k; |
|
||||
y *= k; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> CmykScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <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)); |
|
||||
|
|
||||
Vector128<float> scale = Vector128.Create(maxValue); |
|
||||
|
|
||||
nuint n = values.Component0.Vector128Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector128<float> ctmp = scale - Unsafe.Add(ref srcR, i); |
|
||||
Vector128<float> mtmp = scale - Unsafe.Add(ref srcG, i); |
|
||||
Vector128<float> ytmp = scale - Unsafe.Add(ref srcB, i); |
|
||||
Vector128<float> ktmp = Vector128.Min(ctmp, Vector128.Min(mtmp, ytmp)); |
|
||||
|
|
||||
Vector128<float> kMask = ~Vector128.Equals(ktmp, scale); |
|
||||
Vector128<float> divisor = scale - ktmp; |
|
||||
|
|
||||
ctmp = ((ctmp - ktmp) / divisor) & kMask; |
|
||||
mtmp = ((mtmp - ktmp) / divisor) & kMask; |
|
||||
ytmp = ((ytmp - ktmp) / divisor) & kMask; |
|
||||
|
|
||||
Unsafe.Add(ref destC, i) = scale - (ctmp * scale); |
|
||||
Unsafe.Add(ref destM, i) = scale - (mtmp * scale); |
|
||||
Unsafe.Add(ref destY, i) = scale - (ytmp * scale); |
|
||||
Unsafe.Add(ref destK, i) = scale - ktmp; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,100 +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 SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class CmykVector256 : JpegColorConverterVector256 |
|
||||
{ |
|
||||
public CmykVector256(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
|
|
||||
Vector256<float> 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 *= scale; |
|
||||
c *= k; |
|
||||
m *= k; |
|
||||
y *= k; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> CmykScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <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)); |
|
||||
|
|
||||
Vector256<float> scale = Vector256.Create(maxValue); |
|
||||
|
|
||||
nuint n = values.Component0.Vector256Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector256<float> ctmp = scale - Unsafe.Add(ref srcR, i); |
|
||||
Vector256<float> mtmp = scale - Unsafe.Add(ref srcG, i); |
|
||||
Vector256<float> ytmp = scale - Unsafe.Add(ref srcB, i); |
|
||||
Vector256<float> ktmp = Vector256.Min(ctmp, Vector256.Min(mtmp, ytmp)); |
|
||||
|
|
||||
Vector256<float> kMask = ~Vector256.Equals(ktmp, scale); |
|
||||
Vector256<float> divisor = scale - ktmp; |
|
||||
|
|
||||
ctmp = ((ctmp - ktmp) / divisor) & kMask; |
|
||||
mtmp = ((mtmp - ktmp) / divisor) & kMask; |
|
||||
ytmp = ((ytmp - ktmp) / divisor) & kMask; |
|
||||
|
|
||||
Unsafe.Add(ref destC, i) = scale - (ctmp * scale); |
|
||||
Unsafe.Add(ref destM, i) = scale - (mtmp * scale); |
|
||||
Unsafe.Add(ref destY, i) = scale - (ytmp * scale); |
|
||||
Unsafe.Add(ref destK, i) = scale - ktmp; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,108 +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 SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class CmykVector512 : JpegColorConverterVector512 |
|
||||
{ |
|
||||
public CmykVector512(int precision) |
|
||||
: base(JpegColorSpace.Cmyk, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> CmykScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector512<float> c0Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector512<float> c1Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector512<float> c2Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
ref Vector512<float> c3Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
// Used for the color conversion
|
|
||||
Vector512<float> scale = Vector512.Create(1 / (this.MaximumValue * this.MaximumValue)); |
|
||||
|
|
||||
nuint n = values.Component0.Vector512Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
ref Vector512<float> c = ref Unsafe.Add(ref c0Base, i); |
|
||||
ref Vector512<float> m = ref Unsafe.Add(ref c1Base, i); |
|
||||
ref Vector512<float> y = ref Unsafe.Add(ref c2Base, i); |
|
||||
Vector512<float> k = Unsafe.Add(ref c3Base, i); |
|
||||
|
|
||||
k *= scale; |
|
||||
c *= k; |
|
||||
m *= k; |
|
||||
y *= k; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> ConvertFromRgbVectorized(in values, this.MaximumValue, rLane, gLane, bLane); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) |
|
||||
=> CmykScalar.ConvertToRgbInPlace(values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> CmykScalar.ConvertFromRgb(values, this.MaximumValue, rLane, gLane, bLane); |
|
||||
|
|
||||
internal static void ConvertFromRgbVectorized(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
ref Vector512<float> destC = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector512<float> destM = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector512<float> destY = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
ref Vector512<float> destK = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
ref Vector512<float> srcR = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(rLane)); |
|
||||
ref Vector512<float> srcG = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(gLane)); |
|
||||
ref Vector512<float> srcB = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(bLane)); |
|
||||
|
|
||||
Vector512<float> scale = Vector512.Create(maxValue); |
|
||||
|
|
||||
nuint n = values.Component0.Vector512Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector512<float> ctmp = scale - Unsafe.Add(ref srcR, i); |
|
||||
Vector512<float> mtmp = scale - Unsafe.Add(ref srcG, i); |
|
||||
Vector512<float> ytmp = scale - Unsafe.Add(ref srcB, i); |
|
||||
Vector512<float> ktmp = Vector512.Min(ctmp, Vector512.Min(mtmp, ytmp)); |
|
||||
|
|
||||
Vector512<float> kMask = ~Vector512.Equals(ktmp, scale); |
|
||||
Vector512<float> divisor = scale - ktmp; |
|
||||
|
|
||||
ctmp = ((ctmp - ktmp) / divisor) & kMask; |
|
||||
mtmp = ((mtmp - ktmp) / divisor) & kMask; |
|
||||
ytmp = ((ytmp - ktmp) / divisor) & kMask; |
|
||||
|
|
||||
Unsafe.Add(ref destC, i) = scale - (ctmp * scale); |
|
||||
Unsafe.Add(ref destM, i) = scale - (mtmp * scale); |
|
||||
Unsafe.Add(ref destY, i) = scale - (ytmp * scale); |
|
||||
Unsafe.Add(ref destK, i) = scale - ktmp; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,97 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Buffers; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using SixLabors.ImageSharp.ColorProfiles; |
|
||||
using SixLabors.ImageSharp.ColorProfiles.Icc; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class GrayScaleScalar : JpegColorConverterScalar |
|
||||
{ |
|
||||
public GrayScaleScalar(int precision) |
|
||||
: base(JpegColorSpace.Grayscale, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlace(in ComponentValues values) |
|
||||
=> ConvertToRgbInPlace(in values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> ConvertFromRgbScalar(values, rLane, gLane, bLane); |
|
||||
|
|
||||
internal static void ConvertToRgbInPlace(in ComponentValues values, float maxValue) |
|
||||
{ |
|
||||
ref float c0Base = ref MemoryMarshal.GetReference(values.Component0); |
|
||||
ref float c1Base = ref MemoryMarshal.GetReference(values.Component1); |
|
||||
ref float c2Base = ref MemoryMarshal.GetReference(values.Component2); |
|
||||
|
|
||||
float scale = 1F / maxValue; |
|
||||
for (nuint i = 0; i < (nuint)values.Component0.Length; i++) |
|
||||
{ |
|
||||
float c = Unsafe.Add(ref c0Base, i) * scale; |
|
||||
|
|
||||
Unsafe.Add(ref c0Base, i) = c; |
|
||||
Unsafe.Add(ref c1Base, i) = c; |
|
||||
Unsafe.Add(ref c2Base, i) = c; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static void ConvertToRgbInPlaceWithIcc(Configuration configuration, IccProfile profile, in ComponentValues values, float maxValue) |
|
||||
{ |
|
||||
using IMemoryOwner<float> memoryOwner = configuration.MemoryAllocator.Allocate<float>(values.Component0.Length * 3); |
|
||||
Span<float> packed = memoryOwner.Memory.Span; |
|
||||
|
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
|
|
||||
ref float c0Base = ref MemoryMarshal.GetReference(c0); |
|
||||
ref float c1Base = ref MemoryMarshal.GetReference(c1); |
|
||||
ref float c2Base = ref MemoryMarshal.GetReference(c2); |
|
||||
|
|
||||
float scale = 1F / maxValue; |
|
||||
for (nuint i = 0; i < (nuint)values.Component0.Length; i++) |
|
||||
{ |
|
||||
ref float c = ref Unsafe.Add(ref c0Base, i); |
|
||||
c *= scale; |
|
||||
} |
|
||||
|
|
||||
Span<Y> source = MemoryMarshal.Cast<float, Y>(values.Component0); |
|
||||
Span<Rgb> destination = MemoryMarshal.Cast<float, Rgb>(packed); |
|
||||
|
|
||||
ColorConversionOptions options = new() |
|
||||
{ |
|
||||
SourceIccProfile = profile, |
|
||||
TargetIccProfile = CompactSrgbV4Profile.Profile, |
|
||||
}; |
|
||||
ColorProfileConverter converter = new(options); |
|
||||
converter.Convert<Y, Rgb>(source, destination); |
|
||||
|
|
||||
UnpackDeinterleave3(MemoryMarshal.Cast<float, Vector3>(packed)[..source.Length], c0, c1, c2); |
|
||||
} |
|
||||
|
|
||||
internal static void ConvertFromRgbScalar(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
Span<float> c0 = values.Component0; |
|
||||
|
|
||||
for (int i = 0; i < c0.Length; i++) |
|
||||
{ |
|
||||
// luminosity = (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|
||||
c0[i] = (float)((0.299f * rLane[i]) + (0.587f * gLane[i]) + (0.114f * bLane[i])); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,81 +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 SixLabors.ImageSharp.Common.Helpers; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class GrayScaleVector128 : JpegColorConverterVector128 |
|
||||
{ |
|
||||
public GrayScaleVector128(int precision) |
|
||||
: base(JpegColorSpace.Grayscale, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> GrayScaleScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <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
|
|
||||
Vector128<float> scale = Vector128.Create(1 / this.MaximumValue); |
|
||||
|
|
||||
nuint n = values.Component0.Vector128Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector128<float> c = Unsafe.Add(ref c0Base, i) * scale; |
|
||||
|
|
||||
Unsafe.Add(ref c0Base, i) = c; |
|
||||
Unsafe.Add(ref c1Base, i) = c; |
|
||||
Unsafe.Add(ref c2Base, i) = c; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <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
|
|
||||
Vector128<float> f0299 = Vector128.Create(0.299f); |
|
||||
Vector128<float> f0587 = Vector128.Create(0.587f); |
|
||||
Vector128<float> 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); |
|
||||
|
|
||||
// luminosity = (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|
||||
Unsafe.Add(ref destLuminance, i) = Vector128_.MultiplyAddEstimate(f0299, r, Vector128_.MultiplyAddEstimate(f0587, g, f0114 * b)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,81 +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 SixLabors.ImageSharp.Common.Helpers; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class GrayScaleVector256 : JpegColorConverterVector256 |
|
||||
{ |
|
||||
public GrayScaleVector256(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)); |
|
||||
|
|
||||
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
|
|
||||
Vector256<float> scale = Vector256.Create(1 / this.MaximumValue); |
|
||||
|
|
||||
nuint n = values.Component0.Vector256Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector256<float> c = Unsafe.Add(ref c0Base, i) * scale; |
|
||||
|
|
||||
Unsafe.Add(ref c0Base, i) = c; |
|
||||
Unsafe.Add(ref c1Base, i) = c; |
|
||||
Unsafe.Add(ref c2Base, i) = c; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> GrayScaleScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <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
|
|
||||
Vector256<float> f0299 = Vector256.Create(0.299f); |
|
||||
Vector256<float> f0587 = Vector256.Create(0.587f); |
|
||||
Vector256<float> 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); |
|
||||
|
|
||||
// luminosity = (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|
||||
Unsafe.Add(ref destLuminance, i) = Vector256_.MultiplyAddEstimate(f0299, r, Vector256_.MultiplyAddEstimate(f0587, g, f0114 * b)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,89 +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 SixLabors.ImageSharp.Common.Helpers; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class GrayScaleVector512 : JpegColorConverterVector512 |
|
||||
{ |
|
||||
public GrayScaleVector512(int precision) |
|
||||
: base(JpegColorSpace.Grayscale, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> GrayScaleScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector512<float> c0Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
|
|
||||
ref Vector512<float> c1Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
|
|
||||
ref Vector512<float> c2Base = |
|
||||
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++) |
|
||||
{ |
|
||||
Vector512<float> c = Unsafe.Add(ref c0Base, i) * scale; |
|
||||
|
|
||||
Unsafe.Add(ref c0Base, i) = c; |
|
||||
Unsafe.Add(ref c1Base, i) = c; |
|
||||
Unsafe.Add(ref c2Base, i) = c; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
ref Vector512<float> destLuminance = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
|
|
||||
ref Vector512<float> srcRed = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(rLane)); |
|
||||
ref Vector512<float> srcGreen = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(gLane)); |
|
||||
ref Vector512<float> srcBlue = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(bLane)); |
|
||||
|
|
||||
// Used for the color conversion
|
|
||||
Vector512<float> f0299 = Vector512.Create(0.299f); |
|
||||
Vector512<float> f0587 = Vector512.Create(0.587f); |
|
||||
Vector512<float> f0114 = Vector512.Create(0.114f); |
|
||||
|
|
||||
nuint n = values.Component0.Vector512Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
ref Vector512<float> r = ref Unsafe.Add(ref srcRed, i); |
|
||||
ref Vector512<float> g = ref Unsafe.Add(ref srcGreen, i); |
|
||||
ref Vector512<float> b = ref Unsafe.Add(ref srcBlue, i); |
|
||||
|
|
||||
// luminosity = (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|
||||
Unsafe.Add(ref destLuminance, i) = Vector512_.MultiplyAddEstimate(f0299, r, Vector512_.MultiplyAddEstimate(f0587, g, f0114 * b)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) |
|
||||
=> GrayScaleScalar.ConvertToRgbInPlace(in values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> GrayScaleScalar.ConvertFromRgbScalar(values, rLane, gLane, bLane); |
|
||||
} |
|
||||
} |
|
||||
@ -1,84 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Buffers; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using SixLabors.ImageSharp.ColorProfiles; |
|
||||
using SixLabors.ImageSharp.ColorProfiles.Icc; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class RgbScalar : JpegColorConverterScalar |
|
||||
{ |
|
||||
public RgbScalar(int precision) |
|
||||
: base(JpegColorSpace.RGB, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlace(in ComponentValues values) |
|
||||
=> ConvertToRgbInPlace(values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> ConvertFromRgb(values, rLane, gLane, bLane); |
|
||||
|
|
||||
public static void ConvertToRgbInPlaceWithIcc(Configuration configuration, IccProfile profile, in ComponentValues values, float maxValue) |
|
||||
{ |
|
||||
using IMemoryOwner<float> memoryOwner = configuration.MemoryAllocator.Allocate<float>(values.Component0.Length * 3); |
|
||||
Span<float> packed = memoryOwner.Memory.Span; |
|
||||
|
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
|
|
||||
PackedNormalizeInterleave3(c0, c1, c2, packed, 1F / maxValue); |
|
||||
|
|
||||
Span<Rgb> source = MemoryMarshal.Cast<float, Rgb>(packed); |
|
||||
Span<Rgb> destination = MemoryMarshal.Cast<float, Rgb>(packed); |
|
||||
|
|
||||
ColorConversionOptions options = new() |
|
||||
{ |
|
||||
SourceIccProfile = profile, |
|
||||
TargetIccProfile = CompactSrgbV4Profile.Profile, |
|
||||
}; |
|
||||
ColorProfileConverter converter = new(options); |
|
||||
converter.Convert<Rgb, Rgb>(source, destination); |
|
||||
|
|
||||
UnpackDeinterleave3(MemoryMarshal.Cast<float, Vector3>(packed)[..source.Length], c0, c1, c2); |
|
||||
} |
|
||||
|
|
||||
internal static void ConvertToRgbInPlace(ComponentValues values, float maxValue) |
|
||||
{ |
|
||||
ref float c0Base = ref MemoryMarshal.GetReference(values.Component0); |
|
||||
ref float c1Base = ref MemoryMarshal.GetReference(values.Component1); |
|
||||
ref float c2Base = ref MemoryMarshal.GetReference(values.Component2); |
|
||||
|
|
||||
float scale = 1F / maxValue; |
|
||||
|
|
||||
for (nuint i = 0; i < (nuint)values.Component0.Length; i++) |
|
||||
{ |
|
||||
Unsafe.Add(ref c0Base, i) *= scale; |
|
||||
Unsafe.Add(ref c1Base, i) *= scale; |
|
||||
Unsafe.Add(ref c2Base, i) *= scale; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
internal static void ConvertFromRgb(ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
// TODO: This doesn't seem correct. We should be scaling to the maximum value here.
|
|
||||
rLane.CopyTo(values.Component0); |
|
||||
gLane.CopyTo(values.Component1); |
|
||||
bLane.CopyTo(values.Component2); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,56 +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 SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
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 ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> RgbScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,56 +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 SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
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 ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> RgbScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,64 +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 SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
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/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> RgbScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <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); |
|
||||
} |
|
||||
} |
|
||||
@ -1,118 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Buffers; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using SixLabors.ImageSharp.ColorProfiles; |
|
||||
using SixLabors.ImageSharp.ColorProfiles.Icc; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Color converter for tiff images, which use the jpeg compression and CMYK colorspace.
|
|
||||
/// </summary>
|
|
||||
internal sealed class TiffCmykScalar : JpegColorConverterScalar |
|
||||
{ |
|
||||
public TiffCmykScalar(int precision) |
|
||||
: base(JpegColorSpace.TiffCmyk, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlace(in ComponentValues values) |
|
||||
=> ConvertToRgbInPlace(in values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
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 ConvertToRgbInPlace(in ComponentValues values, float maxValue) |
|
||||
{ |
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
Span<float> c3 = values.Component3; |
|
||||
|
|
||||
float scale = 1 / maxValue; |
|
||||
for (int i = 0; i < c0.Length; i++) |
|
||||
{ |
|
||||
float c = c0[i] * scale; |
|
||||
float m = c1[i] * scale; |
|
||||
float y = c2[i] * scale; |
|
||||
float k = 1 - (c3[i] * scale); |
|
||||
|
|
||||
c0[i] = (1 - c) * k; |
|
||||
c1[i] = (1 - m) * k; |
|
||||
c2[i] = (1 - y) * k; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
Span<float> c = values.Component0; |
|
||||
Span<float> m = values.Component1; |
|
||||
Span<float> y = values.Component2; |
|
||||
Span<float> k = values.Component3; |
|
||||
|
|
||||
for (int i = 0; i < c.Length; i++) |
|
||||
{ |
|
||||
float ctmp = 255F - rLane[i]; |
|
||||
float mtmp = 255F - gLane[i]; |
|
||||
float ytmp = 255F - bLane[i]; |
|
||||
float ktmp = MathF.Min(MathF.Min(ctmp, mtmp), ytmp); |
|
||||
|
|
||||
if (ktmp >= 255F) |
|
||||
{ |
|
||||
ctmp = 0F; |
|
||||
mtmp = 0F; |
|
||||
ytmp = 0F; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
float divisor = 1 / (255F - ktmp); |
|
||||
ctmp = (ctmp - ktmp) * divisor; |
|
||||
mtmp = (mtmp - ktmp) * divisor; |
|
||||
ytmp = (ytmp - ktmp) * divisor; |
|
||||
} |
|
||||
|
|
||||
c[i] = ctmp * maxValue; |
|
||||
m[i] = mtmp * maxValue; |
|
||||
y[i] = ytmp * maxValue; |
|
||||
k[i] = ktmp; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static void ConvertToRgbInPlaceWithIcc(Configuration configuration, IccProfile profile, in ComponentValues values, float maxValue) |
|
||||
{ |
|
||||
using IMemoryOwner<float> memoryOwner = configuration.MemoryAllocator.Allocate<float>(values.Component0.Length * 4); |
|
||||
Span<float> packed = memoryOwner.Memory.Span; |
|
||||
|
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
Span<float> c3 = values.Component3; |
|
||||
|
|
||||
PackedNormalizeInterleave4(c0, c1, c2, c3, packed, maxValue); |
|
||||
|
|
||||
Span<Cmyk> source = MemoryMarshal.Cast<float, Cmyk>(packed); |
|
||||
Span<Rgb> destination = MemoryMarshal.Cast<float, Rgb>(packed)[..source.Length]; |
|
||||
|
|
||||
ColorConversionOptions options = new() |
|
||||
{ |
|
||||
SourceIccProfile = profile, |
|
||||
TargetIccProfile = CompactSrgbV4Profile.Profile, |
|
||||
}; |
|
||||
ColorProfileConverter converter = new(options); |
|
||||
converter.Convert<Cmyk, Rgb>(source, destination); |
|
||||
|
|
||||
UnpackDeinterleave3(MemoryMarshal.Cast<float, Vector3>(packed)[..source.Length], c0, c1, c2); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,99 +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 SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class TiffCmykVector128 : JpegColorConverterVector128 |
|
||||
{ |
|
||||
public TiffCmykVector128(int precision) |
|
||||
: base(JpegColorSpace.TiffCmyk, 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)); |
|
||||
|
|
||||
Vector128<float> scale = Vector128.Create(1 / this.MaximumValue); |
|
||||
|
|
||||
nuint n = values.Component0.Vector128Count<float>(); |
|
||||
for (nuint 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 = Vector128<float>.One - (k * scale); |
|
||||
c = (Vector128<float>.One - (c * scale)) * k; |
|
||||
m = (Vector128<float>.One - (m * scale)) * k; |
|
||||
y = (Vector128<float>.One - (y * scale)) * k; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> TiffCmykScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <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)); |
|
||||
|
|
||||
Vector128<float> scale = Vector128.Create(maxValue); |
|
||||
|
|
||||
nuint n = values.Component0.Vector128Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector128<float> ctmp = scale - Unsafe.Add(ref srcR, i); |
|
||||
Vector128<float> mtmp = scale - Unsafe.Add(ref srcG, i); |
|
||||
Vector128<float> ytmp = scale - Unsafe.Add(ref srcB, i); |
|
||||
Vector128<float> ktmp = Vector128.Min(ctmp, Vector128.Min(mtmp, ytmp)); |
|
||||
|
|
||||
Vector128<float> kMask = ~Vector128.Equals(ktmp, scale); |
|
||||
Vector128<float> divisor = Vector128<float>.One / (scale - ktmp); |
|
||||
|
|
||||
ctmp = ((ctmp - ktmp) * divisor) & kMask; |
|
||||
mtmp = ((mtmp - ktmp) * divisor) & kMask; |
|
||||
ytmp = ((ytmp - ktmp) * divisor) & kMask; |
|
||||
|
|
||||
Unsafe.Add(ref destC, i) = ctmp * scale; |
|
||||
Unsafe.Add(ref destM, i) = mtmp * scale; |
|
||||
Unsafe.Add(ref destY, i) = ytmp * scale; |
|
||||
Unsafe.Add(ref destK, i) = ktmp; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,99 +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 SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class TiffCmykVector256 : JpegColorConverterVector256 |
|
||||
{ |
|
||||
public TiffCmykVector256(int precision) |
|
||||
: base(JpegColorSpace.TiffCmyk, 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)); |
|
||||
|
|
||||
Vector256<float> scale = Vector256.Create(1 / 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 = Vector256<float>.One - (k * scale); |
|
||||
c = (Vector256<float>.One - (c * scale)) * k; |
|
||||
m = (Vector256<float>.One - (m * scale)) * k; |
|
||||
y = (Vector256<float>.One - (y * scale)) * k; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> CmykScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <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)); |
|
||||
|
|
||||
Vector256<float> scale = Vector256.Create(maxValue); |
|
||||
|
|
||||
nuint n = values.Component0.Vector256Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector256<float> ctmp = scale - Unsafe.Add(ref srcR, i); |
|
||||
Vector256<float> mtmp = scale - Unsafe.Add(ref srcG, i); |
|
||||
Vector256<float> ytmp = scale - Unsafe.Add(ref srcB, i); |
|
||||
Vector256<float> ktmp = Vector256.Min(ctmp, Vector256.Min(mtmp, ytmp)); |
|
||||
|
|
||||
Vector256<float> kMask = ~Vector256.Equals(ktmp, scale); |
|
||||
Vector256<float> divisor = Vector256<float>.One / (scale - ktmp); |
|
||||
|
|
||||
ctmp = ((ctmp - ktmp) * divisor) & kMask; |
|
||||
mtmp = ((mtmp - ktmp) * divisor) & kMask; |
|
||||
ytmp = ((ytmp - ktmp) * divisor) & kMask; |
|
||||
|
|
||||
Unsafe.Add(ref destC, i) = ctmp * scale; |
|
||||
Unsafe.Add(ref destM, i) = mtmp * scale; |
|
||||
Unsafe.Add(ref destY, i) = ytmp * scale; |
|
||||
Unsafe.Add(ref destK, i) = ktmp; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,108 +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 SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class TiffCmykVector512 : JpegColorConverterVector512 |
|
||||
{ |
|
||||
public TiffCmykVector512(int precision) |
|
||||
: base(JpegColorSpace.TiffCmyk, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> TiffCmykScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector512<float> c0Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector512<float> c1Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector512<float> c2Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
ref Vector512<float> c3Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
// 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> c = ref Unsafe.Add(ref c0Base, i); |
|
||||
ref Vector512<float> m = ref Unsafe.Add(ref c1Base, i); |
|
||||
ref Vector512<float> y = ref Unsafe.Add(ref c2Base, i); |
|
||||
Vector512<float> k = Unsafe.Add(ref c3Base, i); |
|
||||
|
|
||||
k = Vector512<float>.One - (k * scale); |
|
||||
c = (Vector512<float>.One - (c * scale)) * k; |
|
||||
m = (Vector512<float>.One - (m * scale)) * k; |
|
||||
y = (Vector512<float>.One - (y * scale)) * k; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> ConvertFromRgbVectorized(in values, this.MaximumValue, rLane, gLane, bLane); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) |
|
||||
=> TiffCmykScalar.ConvertToRgbInPlace(values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> TiffCmykScalar.ConvertFromRgb(values, this.MaximumValue, rLane, gLane, bLane); |
|
||||
|
|
||||
internal static void ConvertFromRgbVectorized(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
ref Vector512<float> destC = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector512<float> destM = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector512<float> destY = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
ref Vector512<float> destK = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
ref Vector512<float> srcR = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(rLane)); |
|
||||
ref Vector512<float> srcG = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(gLane)); |
|
||||
ref Vector512<float> srcB = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(bLane)); |
|
||||
|
|
||||
Vector512<float> scale = Vector512.Create(maxValue); |
|
||||
|
|
||||
nuint n = values.Component0.Vector512Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector512<float> ctmp = scale - Unsafe.Add(ref srcR, i); |
|
||||
Vector512<float> mtmp = scale - Unsafe.Add(ref srcG, i); |
|
||||
Vector512<float> ytmp = scale - Unsafe.Add(ref srcB, i); |
|
||||
Vector512<float> ktmp = Vector512.Min(ctmp, Vector512.Min(mtmp, ytmp)); |
|
||||
|
|
||||
Vector512<float> kMask = ~Vector512.Equals(ktmp, scale); |
|
||||
Vector512<float> divisor = Vector512<float>.One / (scale - ktmp); |
|
||||
|
|
||||
ctmp = ((ctmp - ktmp) * divisor) & kMask; |
|
||||
mtmp = ((mtmp - ktmp) * divisor) & kMask; |
|
||||
ytmp = ((ytmp - ktmp) * divisor) & kMask; |
|
||||
|
|
||||
Unsafe.Add(ref destC, i) = ctmp * scale; |
|
||||
Unsafe.Add(ref destM, i) = mtmp * scale; |
|
||||
Unsafe.Add(ref destY, i) = ytmp * scale; |
|
||||
Unsafe.Add(ref destK, i) = ktmp; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,153 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Buffers; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using SixLabors.ImageSharp.ColorProfiles; |
|
||||
using SixLabors.ImageSharp.ColorProfiles.Icc; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Color converter for tiff images, which use the jpeg compression and CMYK colorspace.
|
|
||||
/// </summary>
|
|
||||
internal sealed class TiffYccKScalar : JpegColorConverterScalar |
|
||||
{ |
|
||||
// Derived from ITU-T Rec. T.871
|
|
||||
internal const float RCrMult = 1.402f; |
|
||||
internal const float GCbMult = (float)(0.114 * 1.772 / 0.587); |
|
||||
internal const float GCrMult = (float)(0.299 * 1.402 / 0.587); |
|
||||
internal const float BCbMult = 1.772f; |
|
||||
|
|
||||
public TiffYccKScalar(int precision) |
|
||||
: base(JpegColorSpace.TiffYccK, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlace(in ComponentValues values) |
|
||||
=> ConvertToRgbInPlace(in values, this.MaximumValue, this.HalfValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> ConvertFromRgb(values, this.HalfValue, this.MaximumValue, rLane, gLane, bLane); |
|
||||
|
|
||||
public static void ConvertToRgbInPlace(in ComponentValues values, float maxValue, float halfValue) |
|
||||
{ |
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
Span<float> c3 = values.Component3; |
|
||||
|
|
||||
float scale = 1F / maxValue; |
|
||||
halfValue *= scale; |
|
||||
|
|
||||
for (int i = 0; i < values.Component0.Length; i++) |
|
||||
{ |
|
||||
float y = c0[i] * scale; |
|
||||
float cb = (c1[i] * scale) - halfValue; |
|
||||
float cr = (c2[i] * scale) - halfValue; |
|
||||
float scaledK = 1 - (c3[i] * scale); |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
c0[i] = (y + (RCrMult * cr)) * scaledK; |
|
||||
c1[i] = (y - (GCbMult * cb) - (GCrMult * cr)) * scaledK; |
|
||||
c2[i] = (y + (BCbMult * cb)) * scaledK; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static void ConvertFromRgb(in ComponentValues values, float halfValue, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
Span<float> y = values.Component0; |
|
||||
Span<float> cb = values.Component1; |
|
||||
Span<float> cr = values.Component2; |
|
||||
Span<float> k = values.Component3; |
|
||||
|
|
||||
for (int i = 0; i < cr.Length; i++) |
|
||||
{ |
|
||||
// Scale down to [0-1]
|
|
||||
const float divisor = 1F / 255F; |
|
||||
float r = rLane[i] * divisor; |
|
||||
float g = gLane[i] * divisor; |
|
||||
float b = bLane[i] * divisor; |
|
||||
|
|
||||
float ytmp; |
|
||||
float cbtmp; |
|
||||
float crtmp; |
|
||||
float ktmp = 1F - MathF.Max(r, MathF.Max(g, b)); |
|
||||
|
|
||||
if (ktmp >= 1F) |
|
||||
{ |
|
||||
ytmp = 0F; |
|
||||
cbtmp = 0.5F; |
|
||||
crtmp = 0.5F; |
|
||||
ktmp = maxValue; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
float kmask = 1F / (1F - ktmp); |
|
||||
r *= kmask; |
|
||||
g *= kmask; |
|
||||
b *= kmask; |
|
||||
|
|
||||
// Scale to [0-maxValue]
|
|
||||
ytmp = ((0.299f * r) + (0.587f * g) + (0.114f * b)) * maxValue; |
|
||||
cbtmp = halfValue - (((0.168736f * r) - (0.331264f * g) + (0.5f * b)) * maxValue); |
|
||||
crtmp = halfValue + (((0.5f * r) - (0.418688f * g) - (0.081312f * b)) * maxValue); |
|
||||
ktmp *= maxValue; |
|
||||
} |
|
||||
|
|
||||
y[i] = ytmp; |
|
||||
cb[i] = cbtmp; |
|
||||
cr[i] = crtmp; |
|
||||
k[i] = ktmp; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static void ConvertToRgbInPlaceWithIcc(Configuration configuration, IccProfile profile, in ComponentValues values, float maxValue) |
|
||||
{ |
|
||||
using IMemoryOwner<float> memoryOwner = configuration.MemoryAllocator.Allocate<float>(values.Component0.Length * 4); |
|
||||
Span<float> packed = memoryOwner.Memory.Span; |
|
||||
|
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
Span<float> c3 = values.Component3; |
|
||||
|
|
||||
PackedNormalizeInterleave4(c0, c1, c2, c3, packed, maxValue); |
|
||||
|
|
||||
ColorProfileConverter converter = new(); |
|
||||
Span<Cmyk> source = MemoryMarshal.Cast<float, Cmyk>(packed); |
|
||||
|
|
||||
// YccK is not a defined ICC color space � it's a JPEG-specific encoding used in Adobe-style CMYK JPEGs.
|
|
||||
// ICC profiles expect colorimetric CMYK values, so we must first convert YccK to CMYK using a hardcoded inverse transform.
|
|
||||
// This transform assumes Rec.601 YCbCr coefficients and an inverted K channel.
|
|
||||
//
|
|
||||
// The YccK => Cmyk conversion is independent of any embedded ICC profile.
|
|
||||
// Since the same RGB working space is used during conversion to and from XYZ,
|
|
||||
// colorimetric accuracy is preserved.
|
|
||||
converter.Convert<YccK, Cmyk>(MemoryMarshal.Cast<Cmyk, YccK>(source), source); |
|
||||
|
|
||||
Span<Rgb> destination = MemoryMarshal.Cast<float, Rgb>(packed)[..source.Length]; |
|
||||
|
|
||||
ColorConversionOptions options = new() |
|
||||
{ |
|
||||
SourceIccProfile = profile, |
|
||||
TargetIccProfile = CompactSrgbV4Profile.Profile, |
|
||||
}; |
|
||||
converter = new ColorProfileConverter(options); |
|
||||
converter.Convert<Cmyk, Rgb>(source, destination); |
|
||||
|
|
||||
UnpackDeinterleave3(MemoryMarshal.Cast<float, Vector3>(packed)[..source.Length], c0, c1, c2); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,131 +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 SixLabors.ImageSharp.Common.Helpers; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class TiffYccKVector128 : JpegColorConverterVector128 |
|
||||
{ |
|
||||
public TiffYccKVector128(int precision) |
|
||||
: base(JpegColorSpace.TiffYccK, 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)); |
|
||||
|
|
||||
Vector128<float> scale = Vector128.Create(1F / this.MaximumValue); |
|
||||
Vector128<float> chromaOffset = Vector128.Create(this.HalfValue) * scale; |
|
||||
Vector128<float> rCrMult = Vector128.Create(YCbCrScalar.RCrMult); |
|
||||
Vector128<float> gCbMult = Vector128.Create(-YCbCrScalar.GCbMult); |
|
||||
Vector128<float> gCrMult = Vector128.Create(-YCbCrScalar.GCrMult); |
|
||||
Vector128<float> bCbMult = Vector128.Create(YCbCrScalar.BCbMult); |
|
||||
|
|
||||
nuint n = values.Component0.Vector128Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
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); |
|
||||
ref Vector128<float> c3 = ref Unsafe.Add(ref c3Base, i); |
|
||||
|
|
||||
Vector128<float> y = c0 * scale; |
|
||||
Vector128<float> cb = (c1 * scale) - chromaOffset; |
|
||||
Vector128<float> cr = (c2 * scale) - chromaOffset; |
|
||||
Vector128<float> scaledK = Vector128<float>.One - (c3 * scale); |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
Vector128<float> r = Vector128_.MultiplyAddEstimate(cr, rCrMult, y) * scaledK; |
|
||||
Vector128<float> g = Vector128_.MultiplyAddEstimate(cr, gCrMult, Vector128_.MultiplyAddEstimate(cb, gCbMult, y)) * scaledK; |
|
||||
Vector128<float> b = Vector128_.MultiplyAddEstimate(cb, bCbMult, y) * scaledK; |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> TiffYccKScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
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)); |
|
||||
|
|
||||
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> destK = |
|
||||
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
Vector128<float> maxSourceValue = Vector128.Create(1 / 255F); |
|
||||
Vector128<float> maxSampleValue = Vector128.Create(this.MaximumValue); |
|
||||
Vector128<float> chromaOffset = Vector128.Create(this.HalfValue); |
|
||||
|
|
||||
Vector128<float> f0299 = Vector128.Create(0.299f); |
|
||||
Vector128<float> f0587 = Vector128.Create(0.587f); |
|
||||
Vector128<float> f0114 = Vector128.Create(0.114f); |
|
||||
Vector128<float> fn0168736 = Vector128.Create(-0.168736f); |
|
||||
Vector128<float> fn0331264 = Vector128.Create(-0.331264f); |
|
||||
Vector128<float> fn0418688 = Vector128.Create(-0.418688f); |
|
||||
Vector128<float> fn0081312F = Vector128.Create(-0.081312F); |
|
||||
Vector128<float> f05 = Vector128.Create(0.5f); |
|
||||
|
|
||||
nuint n = values.Component0.Vector128Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector128<float> r = Unsafe.Add(ref srcR, i) * maxSourceValue; |
|
||||
Vector128<float> g = Unsafe.Add(ref srcG, i) * maxSourceValue; |
|
||||
Vector128<float> b = Unsafe.Add(ref srcB, i) * maxSourceValue; |
|
||||
Vector128<float> ktmp = Vector128<float>.One - Vector128.Max(r, Vector128.Min(g, b)); |
|
||||
|
|
||||
Vector128<float> kMask = ~Vector128.Equals(ktmp, Vector128<float>.One); |
|
||||
Vector128<float> divisor = Vector128<float>.One / (Vector128<float>.One - ktmp); |
|
||||
|
|
||||
r = (r * divisor) & kMask; |
|
||||
g = (g * divisor) & kMask; |
|
||||
b = (b * divisor) & kMask; |
|
||||
|
|
||||
// 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 = Vector128_.MultiplyAddEstimate(f0299, r, Vector128_.MultiplyAddEstimate(f0587, g, f0114 * b)); |
|
||||
Vector128<float> cb = chromaOffset + Vector128_.MultiplyAddEstimate(fn0168736, r, Vector128_.MultiplyAddEstimate(fn0331264, g, f05 * b)); |
|
||||
Vector128<float> cr = chromaOffset + Vector128_.MultiplyAddEstimate(f05, r, Vector128_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b)); |
|
||||
|
|
||||
Unsafe.Add(ref destY, i) = y * maxSampleValue; |
|
||||
Unsafe.Add(ref destCb, i) = chromaOffset + (cb * maxSampleValue); |
|
||||
Unsafe.Add(ref destCr, i) = chromaOffset + (cr * maxSampleValue); |
|
||||
Unsafe.Add(ref destK, i) = ktmp * maxSampleValue; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,131 +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 SixLabors.ImageSharp.Common.Helpers; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class TiffYccKVector256 : JpegColorConverterVector256 |
|
||||
{ |
|
||||
public TiffYccKVector256(int precision) |
|
||||
: base(JpegColorSpace.TiffYccK, 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)); |
|
||||
|
|
||||
Vector256<float> scale = Vector256.Create(1F / this.MaximumValue); |
|
||||
Vector256<float> chromaOffset = Vector256.Create(this.HalfValue) * scale; |
|
||||
Vector256<float> rCrMult = Vector256.Create(YCbCrScalar.RCrMult); |
|
||||
Vector256<float> gCbMult = Vector256.Create(-YCbCrScalar.GCbMult); |
|
||||
Vector256<float> gCrMult = Vector256.Create(-YCbCrScalar.GCrMult); |
|
||||
Vector256<float> bCbMult = Vector256.Create(YCbCrScalar.BCbMult); |
|
||||
|
|
||||
nuint n = values.Component0.Vector256Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
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); |
|
||||
ref Vector256<float> c3 = ref Unsafe.Add(ref c3Base, i); |
|
||||
|
|
||||
Vector256<float> y = c0 * scale; |
|
||||
Vector256<float> cb = (c1 * scale) - chromaOffset; |
|
||||
Vector256<float> cr = (c2 * scale) - chromaOffset; |
|
||||
Vector256<float> scaledK = Vector256<float>.One - (c3 * scale); |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
Vector256<float> r = Vector256_.MultiplyAddEstimate(cr, rCrMult, y) * scaledK; |
|
||||
Vector256<float> g = Vector256_.MultiplyAddEstimate(cr, gCrMult, Vector256_.MultiplyAddEstimate(cb, gCbMult, y)) * scaledK; |
|
||||
Vector256<float> b = Vector256_.MultiplyAddEstimate(cb, bCbMult, y) * scaledK; |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> TiffYccKScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
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)); |
|
||||
|
|
||||
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> destK = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
Vector256<float> maxSourceValue = Vector256.Create(255F); |
|
||||
Vector256<float> maxSampleValue = Vector256.Create(this.MaximumValue); |
|
||||
Vector256<float> chromaOffset = Vector256.Create(this.HalfValue); |
|
||||
|
|
||||
Vector256<float> f0299 = Vector256.Create(0.299f); |
|
||||
Vector256<float> f0587 = Vector256.Create(0.587f); |
|
||||
Vector256<float> f0114 = Vector256.Create(0.114f); |
|
||||
Vector256<float> fn0168736 = Vector256.Create(-0.168736f); |
|
||||
Vector256<float> fn0331264 = Vector256.Create(-0.331264f); |
|
||||
Vector256<float> fn0418688 = Vector256.Create(-0.418688f); |
|
||||
Vector256<float> fn0081312F = Vector256.Create(-0.081312F); |
|
||||
Vector256<float> 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) / maxSourceValue; |
|
||||
Vector256<float> g = Unsafe.Add(ref srcG, i) / maxSourceValue; |
|
||||
Vector256<float> b = Unsafe.Add(ref srcB, i) / maxSourceValue; |
|
||||
Vector256<float> ktmp = Vector256<float>.One - Vector256.Max(r, Vector256.Min(g, b)); |
|
||||
|
|
||||
Vector256<float> kMask = ~Vector256.Equals(ktmp, Vector256<float>.One); |
|
||||
Vector256<float> divisor = Vector256<float>.One / (Vector256<float>.One - ktmp); |
|
||||
|
|
||||
r = (r * divisor) & kMask; |
|
||||
g = (g * divisor) & kMask; |
|
||||
b = (b * divisor) & kMask; |
|
||||
|
|
||||
// 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 = Vector256_.MultiplyAddEstimate(f0299, r, Vector256_.MultiplyAddEstimate(f0587, g, f0114 * b)); |
|
||||
Vector256<float> cb = chromaOffset + Vector256_.MultiplyAddEstimate(fn0168736, r, Vector256_.MultiplyAddEstimate(fn0331264, g, f05 * b)); |
|
||||
Vector256<float> cr = chromaOffset + Vector256_.MultiplyAddEstimate(f05, r, Vector256_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b)); |
|
||||
|
|
||||
Unsafe.Add(ref destY, i) = y * maxSampleValue; |
|
||||
Unsafe.Add(ref destCb, i) = chromaOffset + (cb * maxSampleValue); |
|
||||
Unsafe.Add(ref destCr, i) = chromaOffset + (cr * maxSampleValue); |
|
||||
Unsafe.Add(ref destK, i) = ktmp * maxSampleValue; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,142 +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 SixLabors.ImageSharp.Common.Helpers; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class TiffYccKVector512 : JpegColorConverterVector512 |
|
||||
{ |
|
||||
public TiffYccKVector512(int precision) |
|
||||
: base(JpegColorSpace.TiffYccK, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> TiffYccKScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector512<float> c0Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector512<float> c1Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector512<float> c2Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
ref Vector512<float> c3Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
Vector512<float> scale = Vector512.Create(1F / this.MaximumValue); |
|
||||
Vector512<float> chromaOffset = Vector512.Create(this.HalfValue) * scale; |
|
||||
Vector512<float> rCrMult = Vector512.Create(YCbCrScalar.RCrMult); |
|
||||
Vector512<float> gCbMult = Vector512.Create(-YCbCrScalar.GCbMult); |
|
||||
Vector512<float> gCrMult = Vector512.Create(-YCbCrScalar.GCrMult); |
|
||||
Vector512<float> bCbMult = Vector512.Create(YCbCrScalar.BCbMult); |
|
||||
|
|
||||
nuint n = values.Component0.Vector512Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
ref Vector512<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|
||||
ref Vector512<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|
||||
ref Vector512<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|
||||
ref Vector512<float> c3 = ref Unsafe.Add(ref c3Base, i); |
|
||||
|
|
||||
Vector512<float> y = c0 * scale; |
|
||||
Vector512<float> cb = (c1 * scale) - chromaOffset; |
|
||||
Vector512<float> cr = (c2 * scale) - chromaOffset; |
|
||||
Vector512<float> scaledK = Vector512<float>.One - (c3 * scale); |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
Vector512<float> r = Vector512_.MultiplyAddEstimate(cr, rCrMult, y) * scaledK; |
|
||||
Vector512<float> g = Vector512_.MultiplyAddEstimate(cr, gCrMult, Vector512_.MultiplyAddEstimate(cb, gCbMult, y)) * scaledK; |
|
||||
Vector512<float> b = Vector512_.MultiplyAddEstimate(cb, bCbMult, y) * scaledK; |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> ConvertFromRgbVectorized(in values, this.MaximumValue, this.HalfValue, rLane, gLane, bLane); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) |
|
||||
=> TiffYccKScalar.ConvertToRgbInPlace(values, this.MaximumValue, this.HalfValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> TiffYccKScalar.ConvertFromRgb(values, this.HalfValue, this.MaximumValue, rLane, gLane, bLane); |
|
||||
|
|
||||
internal static void ConvertFromRgbVectorized(in ComponentValues values, float maxValue, float halfValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
ref Vector512<float> srcR = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(rLane)); |
|
||||
ref Vector512<float> srcG = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(gLane)); |
|
||||
ref Vector512<float> srcB = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(bLane)); |
|
||||
|
|
||||
ref Vector512<float> destY = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector512<float> destCb = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector512<float> destCr = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
ref Vector512<float> destK = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
Vector512<float> maxSourceValue = Vector512.Create(255F); |
|
||||
Vector512<float> maxSampleValue = Vector512.Create(maxValue); |
|
||||
Vector512<float> chromaOffset = Vector512.Create(halfValue); |
|
||||
|
|
||||
Vector512<float> f0299 = Vector512.Create(0.299f); |
|
||||
Vector512<float> f0587 = Vector512.Create(0.587f); |
|
||||
Vector512<float> f0114 = Vector512.Create(0.114f); |
|
||||
Vector512<float> fn0168736 = Vector512.Create(-0.168736f); |
|
||||
Vector512<float> fn0331264 = Vector512.Create(-0.331264f); |
|
||||
Vector512<float> fn0418688 = Vector512.Create(-0.418688f); |
|
||||
Vector512<float> fn0081312F = Vector512.Create(-0.081312F); |
|
||||
Vector512<float> f05 = Vector512.Create(0.5f); |
|
||||
|
|
||||
nuint n = values.Component0.Vector512Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector512<float> r = Unsafe.Add(ref srcR, i) / maxSourceValue; |
|
||||
Vector512<float> g = Unsafe.Add(ref srcG, i) / maxSourceValue; |
|
||||
Vector512<float> b = Unsafe.Add(ref srcB, i) / maxSourceValue; |
|
||||
Vector512<float> ktmp = Vector512<float>.One - Vector512.Max(r, Vector512.Min(g, b)); |
|
||||
|
|
||||
Vector512<float> kMask = ~Vector512.Equals(ktmp, Vector512<float>.One); |
|
||||
Vector512<float> divisor = Vector512<float>.One / (Vector512<float>.One - ktmp); |
|
||||
|
|
||||
r = (r * divisor) & kMask; |
|
||||
g = (g * divisor) & kMask; |
|
||||
b = (b * divisor) & kMask; |
|
||||
|
|
||||
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|
||||
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
|
|
||||
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
|
|
||||
Vector512<float> y = Vector512_.MultiplyAddEstimate(f0299, r, Vector512_.MultiplyAddEstimate(f0587, g, f0114 * b)); |
|
||||
Vector512<float> cb = chromaOffset + Vector512_.MultiplyAddEstimate(fn0168736, r, Vector512_.MultiplyAddEstimate(fn0331264, g, f05 * b)); |
|
||||
Vector512<float> cr = chromaOffset + Vector512_.MultiplyAddEstimate(f05, r, Vector512_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b)); |
|
||||
|
|
||||
Unsafe.Add(ref destY, i) = y * maxSampleValue; |
|
||||
Unsafe.Add(ref destCb, i) = chromaOffset + (cb * maxSampleValue); |
|
||||
Unsafe.Add(ref destCr, i) = chromaOffset + (cr * maxSampleValue); |
|
||||
Unsafe.Add(ref destK, i) = ktmp * maxSampleValue; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,121 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Buffers; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using SixLabors.ImageSharp.ColorProfiles; |
|
||||
using SixLabors.ImageSharp.ColorProfiles.Icc; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class YCbCrScalar : JpegColorConverterScalar |
|
||||
{ |
|
||||
// derived from ITU-T Rec. T.871
|
|
||||
internal const float RCrMult = 1.402f; |
|
||||
internal const float GCbMult = (float)(0.114 * 1.772 / 0.587); |
|
||||
internal const float GCrMult = (float)(0.299 * 1.402 / 0.587); |
|
||||
internal const float BCbMult = 1.772f; |
|
||||
|
|
||||
public YCbCrScalar(int precision) |
|
||||
: base(JpegColorSpace.YCbCr, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlace(in ComponentValues values) |
|
||||
=> ConvertToRgbInPlace(values, this.MaximumValue, this.HalfValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> ConvertFromRgb(values, this.HalfValue, rLane, gLane, bLane); |
|
||||
|
|
||||
public static void ConvertToRgbInPlace(in ComponentValues values, float maxValue, float halfValue) |
|
||||
{ |
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
|
|
||||
float scale = 1 / maxValue; |
|
||||
|
|
||||
for (int i = 0; i < c0.Length; i++) |
|
||||
{ |
|
||||
float y = c0[i]; |
|
||||
float cb = c1[i] - halfValue; |
|
||||
float cr = c2[i] - halfValue; |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
c0[i] = MathF.Round(y + (RCrMult * cr), MidpointRounding.AwayFromZero) * scale; |
|
||||
c1[i] = MathF.Round(y - (GCbMult * cb) - (GCrMult * cr), MidpointRounding.AwayFromZero) * scale; |
|
||||
c2[i] = MathF.Round(y + (BCbMult * cb), MidpointRounding.AwayFromZero) * scale; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static void ConvertToRgbInPlaceWithIcc(Configuration configuration, IccProfile profile, in ComponentValues values, float maxValue) |
|
||||
{ |
|
||||
using IMemoryOwner<float> memoryOwner = configuration.MemoryAllocator.Allocate<float>(values.Component0.Length * 3); |
|
||||
Span<float> packed = memoryOwner.Memory.Span; |
|
||||
|
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
|
|
||||
// Although YCbCr is a defined ICC color space, in practice ICC profiles
|
|
||||
// do not implement transforms from it.
|
|
||||
// Therefore, we first convert JPEG YCbCr to RGB manually, then perform
|
|
||||
// color-managed conversion to the target profile.
|
|
||||
//
|
|
||||
// The YCbCr => RGB conversion is based on BT.601 and is independent of any embedded ICC profile.
|
|
||||
// Since the same RGB working space is used during conversion to and from XYZ,
|
|
||||
// colorimetric accuracy is preserved.
|
|
||||
ColorProfileConverter converter = new(); |
|
||||
|
|
||||
PackedNormalizeInterleave3(c0, c1, c2, packed, 1F / maxValue); |
|
||||
|
|
||||
Span<YCbCr> source = MemoryMarshal.Cast<float, YCbCr>(packed); |
|
||||
Span<Rgb> destination = MemoryMarshal.Cast<float, Rgb>(packed); |
|
||||
|
|
||||
converter.Convert<YCbCr, Rgb>(source, destination); |
|
||||
|
|
||||
ColorConversionOptions options = new() |
|
||||
{ |
|
||||
SourceIccProfile = profile, |
|
||||
TargetIccProfile = CompactSrgbV4Profile.Profile, |
|
||||
}; |
|
||||
converter = new ColorProfileConverter(options); |
|
||||
converter.Convert<Rgb, Rgb>(destination, destination); |
|
||||
|
|
||||
UnpackDeinterleave3(MemoryMarshal.Cast<float, Vector3>(packed)[..source.Length], c0, c1, c2); |
|
||||
} |
|
||||
|
|
||||
public static void ConvertFromRgb(in ComponentValues values, float halfValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
Span<float> y = values.Component0; |
|
||||
Span<float> cb = values.Component1; |
|
||||
Span<float> cr = values.Component2; |
|
||||
|
|
||||
for (int i = 0; i < y.Length; i++) |
|
||||
{ |
|
||||
float r = rLane[i]; |
|
||||
float g = gLane[i]; |
|
||||
float b = bLane[i]; |
|
||||
|
|
||||
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|
||||
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
|
|
||||
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
|
|
||||
y[i] = (0.299f * r) + (0.587f * g) + (0.114f * b); |
|
||||
cb[i] = halfValue - (0.168736f * r) - (0.331264f * g) + (0.5f * b); |
|
||||
cr[i] = halfValue + (0.5f * r) - (0.418688f * g) - (0.081312f * b); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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 SixLabors.ImageSharp.Common.Helpers; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class YCbCrVector128 : JpegColorConverterVector128 |
|
||||
{ |
|
||||
public YCbCrVector128(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)); |
|
||||
|
|
||||
Vector128<float> chromaOffset = Vector128.Create(-this.HalfValue); |
|
||||
Vector128<float> scale = Vector128.Create(1 / this.MaximumValue); |
|
||||
Vector128<float> rCrMult = Vector128.Create(YCbCrScalar.RCrMult); |
|
||||
Vector128<float> gCbMult = Vector128.Create(-YCbCrScalar.GCbMult); |
|
||||
Vector128<float> gCrMult = Vector128.Create(-YCbCrScalar.GCrMult); |
|
||||
Vector128<float> bCbMult = Vector128.Create(YCbCrScalar.BCbMult); |
|
||||
|
|
||||
// Walking 8 elements at one step:
|
|
||||
nuint n = values.Component0.Vector128Count<float>(); |
|
||||
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 = c1 + chromaOffset; |
|
||||
Vector128<float> cr = c2 + chromaOffset; |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
Vector128<float> r = Vector128_.MultiplyAddEstimate(cr, rCrMult, y); |
|
||||
Vector128<float> g = Vector128_.MultiplyAddEstimate(cr, gCrMult, Vector128_.MultiplyAddEstimate(cb, gCbMult, y)); |
|
||||
Vector128<float> b = Vector128_.MultiplyAddEstimate(cb, bCbMult, y); |
|
||||
|
|
||||
r = Vector128_.RoundToNearestInteger(r) * scale; |
|
||||
g = Vector128_.RoundToNearestInteger(g) * scale; |
|
||||
b = Vector128_.RoundToNearestInteger(b) * scale; |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> YCbCrScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <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)); |
|
||||
|
|
||||
Vector128<float> chromaOffset = Vector128.Create(this.HalfValue); |
|
||||
Vector128<float> f0299 = Vector128.Create(0.299f); |
|
||||
Vector128<float> f0587 = Vector128.Create(0.587f); |
|
||||
Vector128<float> f0114 = Vector128.Create(0.114f); |
|
||||
Vector128<float> fn0168736 = Vector128.Create(-0.168736f); |
|
||||
Vector128<float> fn0331264 = Vector128.Create(-0.331264f); |
|
||||
Vector128<float> fn0418688 = Vector128.Create(-0.418688f); |
|
||||
Vector128<float> fn0081312F = Vector128.Create(-0.081312F); |
|
||||
Vector128<float> f05 = Vector128.Create(0.5f); |
|
||||
|
|
||||
nuint n = values.Component0.Vector128Count<float>(); |
|
||||
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 = Vector128_.MultiplyAddEstimate(f0299, r, Vector128_.MultiplyAddEstimate(f0587, g, f0114 * b)); |
|
||||
Vector128<float> cb = chromaOffset + Vector128_.MultiplyAddEstimate(fn0168736, r, Vector128_.MultiplyAddEstimate(fn0331264, g, f05 * b)); |
|
||||
Vector128<float> cr = chromaOffset + Vector128_.MultiplyAddEstimate(f05, r, Vector128_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b)); |
|
||||
|
|
||||
Unsafe.Add(ref destY, i) = y; |
|
||||
Unsafe.Add(ref destCb, i) = cb; |
|
||||
Unsafe.Add(ref destCr, i) = cr; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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 SixLabors.ImageSharp.Common.Helpers; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class YCbCrVector256 : JpegColorConverterVector256 |
|
||||
{ |
|
||||
public YCbCrVector256(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)); |
|
||||
|
|
||||
Vector256<float> chromaOffset = Vector256.Create(-this.HalfValue); |
|
||||
Vector256<float> scale = Vector256.Create(1 / this.MaximumValue); |
|
||||
Vector256<float> rCrMult = Vector256.Create(YCbCrScalar.RCrMult); |
|
||||
Vector256<float> gCbMult = Vector256.Create(-YCbCrScalar.GCbMult); |
|
||||
Vector256<float> gCrMult = Vector256.Create(-YCbCrScalar.GCrMult); |
|
||||
Vector256<float> 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 = c1 + chromaOffset; |
|
||||
Vector256<float> cr = c2 + chromaOffset; |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
Vector256<float> r = Vector256_.MultiplyAddEstimate(cr, rCrMult, y); |
|
||||
Vector256<float> g = Vector256_.MultiplyAddEstimate(cr, gCrMult, Vector256_.MultiplyAddEstimate(cb, gCbMult, y)); |
|
||||
Vector256<float> b = Vector256_.MultiplyAddEstimate(cb, bCbMult, y); |
|
||||
|
|
||||
r = Vector256_.RoundToNearestInteger(r) * scale; |
|
||||
g = Vector256_.RoundToNearestInteger(g) * scale; |
|
||||
b = Vector256_.RoundToNearestInteger(b) * scale; |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> YCbCrScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <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)); |
|
||||
|
|
||||
Vector256<float> chromaOffset = Vector256.Create(this.HalfValue); |
|
||||
Vector256<float> f0299 = Vector256.Create(0.299f); |
|
||||
Vector256<float> f0587 = Vector256.Create(0.587f); |
|
||||
Vector256<float> f0114 = Vector256.Create(0.114f); |
|
||||
Vector256<float> fn0168736 = Vector256.Create(-0.168736f); |
|
||||
Vector256<float> fn0331264 = Vector256.Create(-0.331264f); |
|
||||
Vector256<float> fn0418688 = Vector256.Create(-0.418688f); |
|
||||
Vector256<float> fn0081312F = Vector256.Create(-0.081312F); |
|
||||
Vector256<float> 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 = Vector256_.MultiplyAddEstimate(f0299, r, Vector256_.MultiplyAddEstimate(f0587, g, f0114 * b)); |
|
||||
Vector256<float> cb = chromaOffset + Vector256_.MultiplyAddEstimate(fn0168736, r, Vector256_.MultiplyAddEstimate(fn0331264, g, f05 * b)); |
|
||||
Vector256<float> cr = chromaOffset + Vector256_.MultiplyAddEstimate(f05, r, Vector256_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b)); |
|
||||
|
|
||||
Unsafe.Add(ref destY, i) = y; |
|
||||
Unsafe.Add(ref destCb, i) = cb; |
|
||||
Unsafe.Add(ref destCr, i) = cr; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,128 +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 SixLabors.ImageSharp.Common.Helpers; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class YCbCrVector512 : JpegColorConverterVector512 |
|
||||
{ |
|
||||
public YCbCrVector512(int precision) |
|
||||
: base(JpegColorSpace.YCbCr, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> YCbCrScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector512<float> c0Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector512<float> c1Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector512<float> c2Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
|
|
||||
Vector512<float> chromaOffset = Vector512.Create(-this.HalfValue); |
|
||||
Vector512<float> scale = Vector512.Create(1 / this.MaximumValue); |
|
||||
Vector512<float> rCrMult = Vector512.Create(YCbCrScalar.RCrMult); |
|
||||
Vector512<float> gCbMult = Vector512.Create(-YCbCrScalar.GCbMult); |
|
||||
Vector512<float> gCrMult = Vector512.Create(-YCbCrScalar.GCrMult); |
|
||||
Vector512<float> bCbMult = Vector512.Create(YCbCrScalar.BCbMult); |
|
||||
|
|
||||
nuint n = values.Component0.Vector512Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
// y = yVals[i];
|
|
||||
// cb = cbVals[i] - 128F;
|
|
||||
// cr = crVals[i] - 128F;
|
|
||||
ref Vector512<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|
||||
ref Vector512<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|
||||
ref Vector512<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|
||||
|
|
||||
Vector512<float> y = c0; |
|
||||
Vector512<float> cb = c1 + chromaOffset; |
|
||||
Vector512<float> cr = c2 + chromaOffset; |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
Vector512<float> r = Vector512_.MultiplyAddEstimate(cr, rCrMult, y); |
|
||||
Vector512<float> g = Vector512_.MultiplyAddEstimate(cr, gCrMult, Vector512_.MultiplyAddEstimate(cb, gCbMult, y)); |
|
||||
Vector512<float> b = Vector512_.MultiplyAddEstimate(cb, bCbMult, y); |
|
||||
|
|
||||
r = Vector512_.RoundToNearestInteger(r) * scale; |
|
||||
g = Vector512_.RoundToNearestInteger(g) * scale; |
|
||||
b = Vector512_.RoundToNearestInteger(b) * scale; |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
ref Vector512<float> destY = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector512<float> destCb = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector512<float> destCr = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
|
|
||||
ref Vector512<float> srcR = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(rLane)); |
|
||||
ref Vector512<float> srcG = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(gLane)); |
|
||||
ref Vector512<float> srcB = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(bLane)); |
|
||||
|
|
||||
Vector512<float> chromaOffset = Vector512.Create(this.HalfValue); |
|
||||
Vector512<float> f0299 = Vector512.Create(0.299f); |
|
||||
Vector512<float> f0587 = Vector512.Create(0.587f); |
|
||||
Vector512<float> f0114 = Vector512.Create(0.114f); |
|
||||
Vector512<float> fn0168736 = Vector512.Create(-0.168736f); |
|
||||
Vector512<float> fn0331264 = Vector512.Create(-0.331264f); |
|
||||
Vector512<float> fn0418688 = Vector512.Create(-0.418688f); |
|
||||
Vector512<float> fn0081312F = Vector512.Create(-0.081312F); |
|
||||
Vector512<float> f05 = Vector512.Create(0.5f); |
|
||||
|
|
||||
nuint n = values.Component0.Vector512Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector512<float> r = Unsafe.Add(ref srcR, i); |
|
||||
Vector512<float> g = Unsafe.Add(ref srcG, i); |
|
||||
Vector512<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)
|
|
||||
Vector512<float> y = Vector512_.MultiplyAddEstimate(f0299, r, Vector512_.MultiplyAddEstimate(f0587, g, f0114 * b)); |
|
||||
Vector512<float> cb = chromaOffset + Vector512_.MultiplyAddEstimate(fn0168736, r, Vector512_.MultiplyAddEstimate(fn0331264, g, f05 * b)); |
|
||||
Vector512<float> cr = chromaOffset + Vector512_.MultiplyAddEstimate(f05, r, Vector512_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b)); |
|
||||
|
|
||||
Unsafe.Add(ref destY, i) = y; |
|
||||
Unsafe.Add(ref destCb, i) = cb; |
|
||||
Unsafe.Add(ref destCr, i) = cr; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) |
|
||||
=> YCbCrScalar.ConvertToRgbInPlace(values, this.MaximumValue, this.HalfValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> YCbCrScalar.ConvertFromRgb(values, this.HalfValue, rLane, gLane, bLane); |
|
||||
} |
|
||||
} |
|
||||
@ -1,125 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Buffers; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using SixLabors.ImageSharp.ColorProfiles; |
|
||||
using SixLabors.ImageSharp.ColorProfiles.Icc; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class YccKScalar : JpegColorConverterScalar |
|
||||
{ |
|
||||
// Derived from ITU-T Rec. T.871
|
|
||||
internal const float RCrMult = 1.402f; |
|
||||
internal const float GCbMult = (float)(0.114 * 1.772 / 0.587); |
|
||||
internal const float GCrMult = (float)(0.299 * 1.402 / 0.587); |
|
||||
internal const float BCbMult = 1.772f; |
|
||||
|
|
||||
public YccKScalar(int precision) |
|
||||
: base(JpegColorSpace.Ycck, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlace(in ComponentValues values) |
|
||||
=> ConvertToRgbInPlace(values, this.MaximumValue, this.HalfValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> ConvertFromRgb(values, this.HalfValue, this.MaximumValue, rLane, gLane, bLane); |
|
||||
|
|
||||
public static void ConvertToRgbInPlace(in ComponentValues values, float maxValue, float halfValue) |
|
||||
{ |
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
Span<float> c3 = values.Component3; |
|
||||
|
|
||||
float scale = 1 / (maxValue * maxValue); |
|
||||
|
|
||||
for (int i = 0; i < values.Component0.Length; i++) |
|
||||
{ |
|
||||
float y = c0[i]; |
|
||||
float cb = c1[i] - halfValue; |
|
||||
float cr = c2[i] - halfValue; |
|
||||
float scaledK = c3[i] * scale; |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
c0[i] = (maxValue - MathF.Round(y + (RCrMult * cr), MidpointRounding.AwayFromZero)) * scaledK; |
|
||||
c1[i] = (maxValue - MathF.Round(y - (GCbMult * cb) - (GCrMult * cr), MidpointRounding.AwayFromZero)) * scaledK; |
|
||||
c2[i] = (maxValue - MathF.Round(y + (BCbMult * cb), MidpointRounding.AwayFromZero)) * scaledK; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static void ConvertFromRgb(in ComponentValues values, float halfValue, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
// rgb -> cmyk
|
|
||||
CmykScalar.ConvertFromRgb(in values, maxValue, rLane, gLane, bLane); |
|
||||
|
|
||||
// cmyk -> ycck
|
|
||||
Span<float> c = values.Component0; |
|
||||
Span<float> m = values.Component1; |
|
||||
Span<float> y = values.Component2; |
|
||||
|
|
||||
for (int i = 0; i < y.Length; i++) |
|
||||
{ |
|
||||
float r = maxValue - c[i]; |
|
||||
float g = maxValue - m[i]; |
|
||||
float b = maxValue - y[i]; |
|
||||
|
|
||||
// k value is passed untouched from rgb -> cmyk conversion
|
|
||||
c[i] = (0.299f * r) + (0.587f * g) + (0.114f * b); |
|
||||
m[i] = halfValue - (0.168736f * r) - (0.331264f * g) + (0.5f * b); |
|
||||
y[i] = halfValue + (0.5f * r) - (0.418688f * g) - (0.081312f * b); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static void ConvertToRgbInPlaceWithIcc(Configuration configuration, IccProfile profile, in ComponentValues values, float maxValue) |
|
||||
{ |
|
||||
using IMemoryOwner<float> memoryOwner = configuration.MemoryAllocator.Allocate<float>(values.Component0.Length * 4); |
|
||||
Span<float> packed = memoryOwner.Memory.Span; |
|
||||
|
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
Span<float> c3 = values.Component3; |
|
||||
|
|
||||
PackedInvertNormalizeInterleave4(c0, c1, c2, c3, packed, maxValue); |
|
||||
|
|
||||
ColorProfileConverter converter = new(); |
|
||||
Span<Cmyk> source = MemoryMarshal.Cast<float, Cmyk>(packed); |
|
||||
|
|
||||
// YccK is not a defined ICC color space — it's a JPEG-specific encoding used in Adobe-style CMYK JPEGs.
|
|
||||
// ICC profiles expect colorimetric CMYK values, so we must first convert YccK to CMYK using a hardcoded inverse transform.
|
|
||||
// This transform assumes Rec.601 YCbCr coefficients and an inverted K channel.
|
|
||||
//
|
|
||||
// The YccK => Cmyk conversion is independent of any embedded ICC profile.
|
|
||||
// Since the same RGB working space is used during conversion to and from XYZ,
|
|
||||
// colorimetric accuracy is preserved.
|
|
||||
converter.Convert<YccK, Cmyk>(MemoryMarshal.Cast<Cmyk, YccK>(source), source); |
|
||||
|
|
||||
Span<Rgb> destination = MemoryMarshal.Cast<float, Rgb>(packed)[..source.Length]; |
|
||||
|
|
||||
ColorConversionOptions options = new() |
|
||||
{ |
|
||||
SourceIccProfile = profile, |
|
||||
TargetIccProfile = CompactSrgbV4Profile.Profile, |
|
||||
}; |
|
||||
converter = new ColorProfileConverter(options); |
|
||||
converter.Convert<Cmyk, Rgb>(source, destination); |
|
||||
|
|
||||
UnpackDeinterleave3(MemoryMarshal.Cast<float, Vector3>(packed)[..source.Length], c0, c1, c2); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,135 +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 SixLabors.ImageSharp.Common.Helpers; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class YccKVector128 : JpegColorConverterVector128 |
|
||||
{ |
|
||||
public YccKVector128(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
|
|
||||
Vector128<float> chromaOffset = Vector128.Create(-this.HalfValue); |
|
||||
Vector128<float> scale = Vector128.Create(1 / (this.MaximumValue * this.MaximumValue)); |
|
||||
Vector128<float> max = Vector128.Create(this.MaximumValue); |
|
||||
Vector128<float> rCrMult = Vector128.Create(YCbCrScalar.RCrMult); |
|
||||
Vector128<float> gCbMult = Vector128.Create(-YCbCrScalar.GCbMult); |
|
||||
Vector128<float> gCrMult = Vector128.Create(-YCbCrScalar.GCrMult); |
|
||||
Vector128<float> bCbMult = Vector128.Create(YCbCrScalar.BCbMult); |
|
||||
|
|
||||
// Walking 8 elements at one step:
|
|
||||
nuint n = values.Component0.Vector128Count<float>(); |
|
||||
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 = c1 + chromaOffset; |
|
||||
Vector128<float> cr = c2 + chromaOffset; |
|
||||
Vector128<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);
|
|
||||
Vector128<float> r = Vector128_.MultiplyAddEstimate(cr, rCrMult, y); |
|
||||
Vector128<float> g = Vector128_.MultiplyAddEstimate(cr, gCrMult, Vector128_.MultiplyAddEstimate(cb, gCbMult, y)); |
|
||||
Vector128<float> b = Vector128_.MultiplyAddEstimate(cb, bCbMult, y); |
|
||||
|
|
||||
r = max - Vector128_.RoundToNearestInteger(r); |
|
||||
g = max - Vector128_.RoundToNearestInteger(g); |
|
||||
b = max - Vector128_.RoundToNearestInteger(b); |
|
||||
|
|
||||
r *= scaledK; |
|
||||
g *= scaledK; |
|
||||
b *= scaledK; |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> YccKScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
// rgb -> cmyk
|
|
||||
CmykVector128.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
|
|
||||
Vector128<float> maxSampleValue = Vector128.Create(this.MaximumValue); |
|
||||
|
|
||||
Vector128<float> chromaOffset = Vector128.Create(this.HalfValue); |
|
||||
|
|
||||
Vector128<float> f0299 = Vector128.Create(0.299f); |
|
||||
Vector128<float> f0587 = Vector128.Create(0.587f); |
|
||||
Vector128<float> f0114 = Vector128.Create(0.114f); |
|
||||
Vector128<float> fn0168736 = Vector128.Create(-0.168736f); |
|
||||
Vector128<float> fn0331264 = Vector128.Create(-0.331264f); |
|
||||
Vector128<float> fn0418688 = Vector128.Create(-0.418688f); |
|
||||
Vector128<float> fn0081312F = Vector128.Create(-0.081312F); |
|
||||
Vector128<float> f05 = Vector128.Create(0.5f); |
|
||||
|
|
||||
nuint n = values.Component0.Vector128Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector128<float> r = maxSampleValue - Unsafe.Add(ref srcR, i); |
|
||||
Vector128<float> g = maxSampleValue - Unsafe.Add(ref srcG, i); |
|
||||
Vector128<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)
|
|
||||
Vector128<float> y = Vector128_.MultiplyAddEstimate(f0299, r, Vector128_.MultiplyAddEstimate(f0587, g, f0114 * b)); |
|
||||
Vector128<float> cb = chromaOffset + Vector128_.MultiplyAddEstimate(fn0168736, r, Vector128_.MultiplyAddEstimate(fn0331264, g, f05 * b)); |
|
||||
Vector128<float> cr = chromaOffset + Vector128_.MultiplyAddEstimate(f05, r, Vector128_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b)); |
|
||||
|
|
||||
Unsafe.Add(ref destY, i) = y; |
|
||||
Unsafe.Add(ref destCb, i) = cb; |
|
||||
Unsafe.Add(ref destCr, i) = cr; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,135 +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 SixLabors.ImageSharp.Common.Helpers; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class YccKVector256 : JpegColorConverterVector256 |
|
||||
{ |
|
||||
public YccKVector256(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
|
|
||||
Vector256<float> chromaOffset = Vector256.Create(-this.HalfValue); |
|
||||
Vector256<float> scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue)); |
|
||||
Vector256<float> max = Vector256.Create(this.MaximumValue); |
|
||||
Vector256<float> rCrMult = Vector256.Create(YCbCrScalar.RCrMult); |
|
||||
Vector256<float> gCbMult = Vector256.Create(-YCbCrScalar.GCbMult); |
|
||||
Vector256<float> gCrMult = Vector256.Create(-YCbCrScalar.GCrMult); |
|
||||
Vector256<float> 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 = c1 + chromaOffset; |
|
||||
Vector256<float> cr = c2 + chromaOffset; |
|
||||
Vector256<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);
|
|
||||
Vector256<float> r = Vector256_.MultiplyAddEstimate(cr, rCrMult, y); |
|
||||
Vector256<float> g = Vector256_.MultiplyAddEstimate(cr, gCrMult, Vector256_.MultiplyAddEstimate(cb, gCbMult, y)); |
|
||||
Vector256<float> b = Vector256_.MultiplyAddEstimate(cb, bCbMult, y); |
|
||||
|
|
||||
r = max - Vector256_.RoundToNearestInteger(r); |
|
||||
g = max - Vector256_.RoundToNearestInteger(g); |
|
||||
b = max - Vector256_.RoundToNearestInteger(b); |
|
||||
|
|
||||
r *= scaledK; |
|
||||
g *= scaledK; |
|
||||
b *= scaledK; |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> YccKScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
// rgb -> cmyk
|
|
||||
CmykVector256.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
|
|
||||
Vector256<float> maxSampleValue = Vector256.Create(this.MaximumValue); |
|
||||
|
|
||||
Vector256<float> chromaOffset = Vector256.Create(this.HalfValue); |
|
||||
|
|
||||
Vector256<float> f0299 = Vector256.Create(0.299f); |
|
||||
Vector256<float> f0587 = Vector256.Create(0.587f); |
|
||||
Vector256<float> f0114 = Vector256.Create(0.114f); |
|
||||
Vector256<float> fn0168736 = Vector256.Create(-0.168736f); |
|
||||
Vector256<float> fn0331264 = Vector256.Create(-0.331264f); |
|
||||
Vector256<float> fn0418688 = Vector256.Create(-0.418688f); |
|
||||
Vector256<float> fn0081312F = Vector256.Create(-0.081312F); |
|
||||
Vector256<float> f05 = Vector256.Create(0.5f); |
|
||||
|
|
||||
nuint n = values.Component0.Vector256Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector256<float> r = maxSampleValue - Unsafe.Add(ref srcR, i); |
|
||||
Vector256<float> g = maxSampleValue - Unsafe.Add(ref srcG, i); |
|
||||
Vector256<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)
|
|
||||
Vector256<float> y = Vector256_.MultiplyAddEstimate(f0299, r, Vector256_.MultiplyAddEstimate(f0587, g, f0114 * b)); |
|
||||
Vector256<float> cb = chromaOffset + Vector256_.MultiplyAddEstimate(fn0168736, r, Vector256_.MultiplyAddEstimate(fn0331264, g, f05 * b)); |
|
||||
Vector256<float> cr = chromaOffset + Vector256_.MultiplyAddEstimate(f05, r, Vector256_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b)); |
|
||||
|
|
||||
Unsafe.Add(ref destY, i) = y; |
|
||||
Unsafe.Add(ref destCb, i) = cb; |
|
||||
Unsafe.Add(ref destCr, i) = cr; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,143 +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 SixLabors.ImageSharp.Common.Helpers; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class YccKVector512 : JpegColorConverterVector512 |
|
||||
{ |
|
||||
public YccKVector512(int precision) |
|
||||
: base(JpegColorSpace.Ycck, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector512<float> c0Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector512<float> c1Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector512<float> c2Base = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
ref Vector512<float> kBase = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
// Used for the color conversion
|
|
||||
Vector512<float> chromaOffset = Vector512.Create(-this.HalfValue); |
|
||||
Vector512<float> scale = Vector512.Create(1 / (this.MaximumValue * this.MaximumValue)); |
|
||||
Vector512<float> max = Vector512.Create(this.MaximumValue); |
|
||||
Vector512<float> rCrMult = Vector512.Create(YCbCrScalar.RCrMult); |
|
||||
Vector512<float> gCbMult = Vector512.Create(-YCbCrScalar.GCbMult); |
|
||||
Vector512<float> gCrMult = Vector512.Create(-YCbCrScalar.GCrMult); |
|
||||
Vector512<float> bCbMult = Vector512.Create(YCbCrScalar.BCbMult); |
|
||||
|
|
||||
// Walking 8 elements at one step:
|
|
||||
nuint n = values.Component0.Vector512Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
// y = yVals[i];
|
|
||||
// cb = cbVals[i] - 128F;
|
|
||||
// cr = crVals[i] - 128F;
|
|
||||
// k = kVals[i] / 256F;
|
|
||||
ref Vector512<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|
||||
ref Vector512<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|
||||
ref Vector512<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|
||||
Vector512<float> y = c0; |
|
||||
Vector512<float> cb = c1 + chromaOffset; |
|
||||
Vector512<float> cr = c2 + chromaOffset; |
|
||||
Vector512<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);
|
|
||||
Vector512<float> r = Vector512_.MultiplyAddEstimate(cr, rCrMult, y); |
|
||||
Vector512<float> g = Vector512_.MultiplyAddEstimate(cr, gCrMult, Vector512_.MultiplyAddEstimate(cb, gCbMult, y)); |
|
||||
Vector512<float> b = Vector512_.MultiplyAddEstimate(cb, bCbMult, y); |
|
||||
|
|
||||
r = max - Vector512_.RoundToNearestInteger(r); |
|
||||
g = max - Vector512_.RoundToNearestInteger(g); |
|
||||
b = max - Vector512_.RoundToNearestInteger(b); |
|
||||
|
|
||||
r *= scaledK; |
|
||||
g *= scaledK; |
|
||||
b *= scaledK; |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile) |
|
||||
=> YccKScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values) |
|
||||
=> YccKScalar.ConvertToRgbInPlace(values, this.MaximumValue, this.HalfValue); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
// rgb -> cmyk
|
|
||||
CmykVector512.ConvertFromRgbVectorized(in values, this.MaximumValue, rLane, gLane, bLane); |
|
||||
|
|
||||
// cmyk -> ycck
|
|
||||
ref Vector512<float> destY = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector512<float> destCb = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector512<float> destCr = |
|
||||
ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
|
|
||||
ref Vector512<float> srcR = ref destY; |
|
||||
ref Vector512<float> srcG = ref destCb; |
|
||||
ref Vector512<float> srcB = ref destCr; |
|
||||
|
|
||||
// Used for the color conversion
|
|
||||
Vector512<float> maxSampleValue = Vector512.Create(this.MaximumValue); |
|
||||
|
|
||||
Vector512<float> chromaOffset = Vector512.Create(this.HalfValue); |
|
||||
|
|
||||
Vector512<float> f0299 = Vector512.Create(0.299f); |
|
||||
Vector512<float> f0587 = Vector512.Create(0.587f); |
|
||||
Vector512<float> f0114 = Vector512.Create(0.114f); |
|
||||
Vector512<float> fn0168736 = Vector512.Create(-0.168736f); |
|
||||
Vector512<float> fn0331264 = Vector512.Create(-0.331264f); |
|
||||
Vector512<float> fn0418688 = Vector512.Create(-0.418688f); |
|
||||
Vector512<float> fn0081312F = Vector512.Create(-0.081312F); |
|
||||
Vector512<float> f05 = Vector512.Create(0.5f); |
|
||||
|
|
||||
nuint n = values.Component0.Vector512Count<float>(); |
|
||||
for (nuint i = 0; i < n; i++) |
|
||||
{ |
|
||||
Vector512<float> r = maxSampleValue - Unsafe.Add(ref srcR, i); |
|
||||
Vector512<float> g = maxSampleValue - Unsafe.Add(ref srcG, i); |
|
||||
Vector512<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)
|
|
||||
Vector512<float> y = Vector512_.MultiplyAddEstimate(f0299, r, Vector512_.MultiplyAddEstimate(f0587, g, f0114 * b)); |
|
||||
Vector512<float> cb = chromaOffset + Vector512_.MultiplyAddEstimate(fn0168736, r, Vector512_.MultiplyAddEstimate(fn0331264, g, f05 * b)); |
|
||||
Vector512<float> cr = chromaOffset + Vector512_.MultiplyAddEstimate(f05, r, Vector512_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b)); |
|
||||
|
|
||||
Unsafe.Add(ref destY, i) = y; |
|
||||
Unsafe.Add(ref destCb, i) = cb; |
|
||||
Unsafe.Add(ref destCr, i) = cr; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
=> YccKScalar.ConvertFromRgb(in values, this.HalfValue, this.MaximumValue, rLane, gLane, bLane); |
|
||||
} |
|
||||
} |
|
||||
@ -1,23 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
|
|
||||
/// based on scalar instructions.
|
|
||||
/// </summary>
|
|
||||
internal abstract class JpegColorConverterScalar : JpegColorConverterBase |
|
||||
{ |
|
||||
protected JpegColorConverterScalar(JpegColorSpace colorSpace, int precision) |
|
||||
: base(colorSpace, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public sealed override bool IsAvailable => true; |
|
||||
|
|
||||
public sealed override int ElementsPerBatch => 1; |
|
||||
} |
|
||||
} |
|
||||
@ -1,129 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Numerics; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
|
|
||||
/// based on <see cref="Vector"/> API.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Converters of this family can work with data of any size.
|
|
||||
/// Even though real life data is guaranteed to be of size
|
|
||||
/// divisible by 8 newer SIMD instructions like AVX512 won't work with
|
|
||||
/// such data out of the box. These converters have fallback code
|
|
||||
/// for 'remainder' data.
|
|
||||
/// </remarks>
|
|
||||
internal abstract class JpegColorConverterVector : JpegColorConverterBase |
|
||||
{ |
|
||||
protected JpegColorConverterVector(JpegColorSpace colorSpace, int precision) |
|
||||
: base(colorSpace, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether this converter is supported on current hardware.
|
|
||||
/// </summary>
|
|
||||
public static bool IsSupported => Vector.IsHardwareAccelerated && Vector<float>.Count % 4 == 0; |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public sealed override bool IsAvailable => IsSupported; |
|
||||
|
|
||||
public override int ElementsPerBatch => Vector<float>.Count; |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public sealed override void ConvertToRgbInPlace(in ComponentValues values) |
|
||||
{ |
|
||||
DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware."); |
|
||||
|
|
||||
int length = values.Component0.Length; |
|
||||
int remainder = (int)((uint)length % (uint)Vector<float>.Count); |
|
||||
|
|
||||
int simdCount = length - remainder; |
|
||||
if (simdCount > 0) |
|
||||
{ |
|
||||
this.ConvertToRgbInPlaceVectorized(values.Slice(0, simdCount)); |
|
||||
} |
|
||||
|
|
||||
// Jpeg images width is always divisible by 8 without a remainder
|
|
||||
// so it's safe to say SSE/AVX1/AVX2 implementations would never have
|
|
||||
// 'remainder' pixels
|
|
||||
// But some exotic simd implementations e.g. AVX-512 can have
|
|
||||
// remainder pixels
|
|
||||
if (remainder > 0) |
|
||||
{ |
|
||||
this.ConvertToRgbInPlaceScalarRemainder(values.Slice(simdCount, remainder)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public sealed override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware."); |
|
||||
|
|
||||
int length = values.Component0.Length; |
|
||||
int remainder = (int)((uint)length % (uint)Vector<float>.Count); |
|
||||
|
|
||||
int simdCount = length - remainder; |
|
||||
if (simdCount > 0) |
|
||||
{ |
|
||||
this.ConvertFromRgbVectorized( |
|
||||
values.Slice(0, simdCount), |
|
||||
rLane[..simdCount], |
|
||||
gLane[..simdCount], |
|
||||
bLane[..simdCount]); |
|
||||
} |
|
||||
|
|
||||
// Jpeg images width is always divisible by 8 without a remainder
|
|
||||
// so it's safe to say SSE/AVX1/AVX2 implementations would never have
|
|
||||
// 'remainder' pixels
|
|
||||
// But some exotic simd implementations e.g. AVX-512 can have
|
|
||||
// remainder pixels
|
|
||||
if (remainder > 0) |
|
||||
{ |
|
||||
this.ConvertFromRgbScalarRemainder( |
|
||||
values.Slice(simdCount, remainder), |
|
||||
rLane.Slice(simdCount, remainder), |
|
||||
gLane.Slice(simdCount, remainder), |
|
||||
bLane.Slice(simdCount, remainder)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts planar jpeg component values in <paramref name="values"/>
|
|
||||
/// to RGB color space in place using <see cref="Vector"/> API.
|
|
||||
/// </summary>
|
|
||||
/// <param name="values">The input/output as a stack-only <see cref="ComponentValues"/> struct</param>
|
|
||||
protected abstract void ConvertToRgbInPlaceVectorized(in ComponentValues values); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts remainder of the planar jpeg component values after
|
|
||||
/// conversion in <see cref="ConvertToRgbInPlaceVectorized(in ComponentValues)"/>.
|
|
||||
/// </summary>
|
|
||||
/// <param name="values">The input/output as a stack-only <see cref="ComponentValues"/> struct</param>
|
|
||||
protected abstract void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts RGB lanes to jpeg component values using <see cref="Vector"/> API.
|
|
||||
/// </summary>
|
|
||||
/// <param name="values">Jpeg component values.</param>
|
|
||||
/// <param name="rLane">Red colors lane.</param>
|
|
||||
/// <param name="gLane">Green colors lane.</param>
|
|
||||
/// <param name="bLane">Blue colors lane.</param>
|
|
||||
protected abstract void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts remainder of RGB lanes to jpeg component values after
|
|
||||
/// conversion in <see cref="ConvertFromRgbVectorized(in ComponentValues, Span{float}, Span{float}, Span{float})"/>.
|
|
||||
/// </summary>
|
|
||||
/// <param name="values">Jpeg component values.</param>
|
|
||||
/// <param name="rLane">Red colors lane.</param>
|
|
||||
/// <param name="gLane">Green colors lane.</param>
|
|
||||
/// <param name="bLane">Blue colors lane.</param>
|
|
||||
protected abstract void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane); |
|
||||
} |
|
||||
} |
|
||||
@ -1,34 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Runtime.Intrinsics; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
|
|
||||
/// based on <see cref="Vector128{T}"/> 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 JpegColorConverterVector128 : JpegColorConverterBase |
|
||||
{ |
|
||||
protected JpegColorConverterVector128(JpegColorSpace colorSpace, int precision) |
|
||||
: base(colorSpace, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public static bool IsSupported => Vector128.IsHardwareAccelerated; |
|
||||
|
|
||||
public sealed override bool IsAvailable => IsSupported; |
|
||||
|
|
||||
public sealed override int ElementsPerBatch => Vector128<float>.Count; |
|
||||
} |
|
||||
} |
|
||||
@ -1,34 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Runtime.Intrinsics; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
|
|
||||
/// based on <see cref="Vector256{T}"/> 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 JpegColorConverterVector256 : JpegColorConverterBase |
|
||||
{ |
|
||||
protected JpegColorConverterVector256(JpegColorSpace colorSpace, int precision) |
|
||||
: base(colorSpace, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public static bool IsSupported => Vector256.IsHardwareAccelerated; |
|
||||
|
|
||||
public sealed override bool IsAvailable => IsSupported; |
|
||||
|
|
||||
public sealed override int ElementsPerBatch => Vector256<float>.Count; |
|
||||
} |
|
||||
} |
|
||||
@ -1,111 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Numerics; |
|
||||
using System.Runtime.Intrinsics; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
|
|
||||
/// based on <see cref="Vector512{T}"/> instructions.
|
|
||||
/// </summary>
|
|
||||
internal abstract class JpegColorConverterVector512 : JpegColorConverterBase |
|
||||
{ |
|
||||
protected JpegColorConverterVector512(JpegColorSpace colorSpace, int precision) |
|
||||
: base(colorSpace, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public static bool IsSupported => Vector512.IsHardwareAccelerated; |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override bool IsAvailable => IsSupported; |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override int ElementsPerBatch => Vector512<float>.Count; |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public sealed override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|
||||
{ |
|
||||
DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware."); |
|
||||
|
|
||||
int length = values.Component0.Length; |
|
||||
int remainder = (int)((uint)length % (uint)Vector512<float>.Count); |
|
||||
|
|
||||
int simdCount = length - remainder; |
|
||||
if (simdCount > 0) |
|
||||
{ |
|
||||
this.ConvertFromRgbVectorized( |
|
||||
values.Slice(0, simdCount), |
|
||||
rLane[..simdCount], |
|
||||
gLane[..simdCount], |
|
||||
bLane[..simdCount]); |
|
||||
} |
|
||||
|
|
||||
if (remainder > 0) |
|
||||
{ |
|
||||
this.ConvertFromRgbScalarRemainder( |
|
||||
values.Slice(simdCount, remainder), |
|
||||
rLane.Slice(simdCount, remainder), |
|
||||
gLane.Slice(simdCount, remainder), |
|
||||
bLane.Slice(simdCount, remainder)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public sealed override void ConvertToRgbInPlace(in ComponentValues values) |
|
||||
{ |
|
||||
DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware."); |
|
||||
|
|
||||
int length = values.Component0.Length; |
|
||||
int remainder = (int)((uint)length % (uint)Vector512<float>.Count); |
|
||||
|
|
||||
int simdCount = length - remainder; |
|
||||
if (simdCount > 0) |
|
||||
{ |
|
||||
this.ConvertToRgbInPlaceVectorized(values.Slice(0, simdCount)); |
|
||||
} |
|
||||
|
|
||||
if (remainder > 0) |
|
||||
{ |
|
||||
this.ConvertToRgbInPlaceScalarRemainder(values.Slice(simdCount, remainder)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts planar jpeg component values in <paramref name="values"/>
|
|
||||
/// to RGB color space in place using <see cref="Vector"/> API.
|
|
||||
/// </summary>
|
|
||||
/// <param name="values">The input/output as a stack-only <see cref="ComponentValues"/> struct</param>
|
|
||||
protected abstract void ConvertToRgbInPlaceVectorized(in ComponentValues values); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts remainder of the planar jpeg component values after
|
|
||||
/// conversion in <see cref="ConvertToRgbInPlaceVectorized(in ComponentValues)"/>.
|
|
||||
/// </summary>
|
|
||||
/// <param name="values">The input/output as a stack-only <see cref="ComponentValues"/> struct</param>
|
|
||||
protected abstract void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts RGB lanes to jpeg component values using <see cref="Vector"/> API.
|
|
||||
/// </summary>
|
|
||||
/// <param name="values">Jpeg component values.</param>
|
|
||||
/// <param name="rLane">Red colors lane.</param>
|
|
||||
/// <param name="gLane">Green colors lane.</param>
|
|
||||
/// <param name="bLane">Blue colors lane.</param>
|
|
||||
protected abstract void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts remainder of RGB lanes to jpeg component values after
|
|
||||
/// conversion in <see cref="ConvertFromRgbVectorized(in ComponentValues, Span{float}, Span{float}, Span{float})"/>.
|
|
||||
/// </summary>
|
|
||||
/// <param name="values">Jpeg component values.</param>
|
|
||||
/// <param name="rLane">Red colors lane.</param>
|
|
||||
/// <param name="gLane">Green colors lane.</param>
|
|
||||
/// <param name="bLane">Blue colors lane.</param>
|
|
||||
protected abstract void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane); |
|
||||
} |
|
||||
} |
|
||||
@ -1,239 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using BenchmarkDotNet.Attributes; |
|
||||
using BenchmarkDotNet.Columns; |
|
||||
using BenchmarkDotNet.Configs; |
|
||||
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Compares each shared operator converter with the Vector512 converter it replaces.
|
|
||||
/// </summary>
|
|
||||
[Config(typeof(Config.Standard))] |
|
||||
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] |
|
||||
[CategoriesColumn] |
|
||||
public class JpegColorConverterOperatorComparison |
|
||||
{ |
|
||||
private JpegColorConverterBase legacy; |
|
||||
private JpegColorConverterBase operatorConverter; |
|
||||
private float[] legacyC0; |
|
||||
private float[] legacyC1; |
|
||||
private float[] legacyC2; |
|
||||
private float[] legacyC3; |
|
||||
private float[] operatorC0; |
|
||||
private float[] operatorC1; |
|
||||
private float[] operatorC2; |
|
||||
private float[] operatorC3; |
|
||||
private float[] r; |
|
||||
private float[] g; |
|
||||
private float[] b; |
|
||||
private int componentCount; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the color model measured by the current benchmark case.
|
|
||||
/// </summary>
|
|
||||
[Params( |
|
||||
JpegColorModel.Grayscale, |
|
||||
JpegColorModel.Rgb, |
|
||||
JpegColorModel.Cmyk, |
|
||||
JpegColorModel.YCbCr, |
|
||||
JpegColorModel.YccK, |
|
||||
JpegColorModel.TiffCmyk, |
|
||||
JpegColorModel.TiffYccK)] |
|
||||
public JpegColorModel ColorModel { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the number of pixels converted by each invocation.
|
|
||||
/// </summary>
|
|
||||
[Params(128, 1024)] |
|
||||
public int Count { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates equivalent legacy and operator converters and their independent component buffers.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
(JpegColorConverterBase Legacy, JpegColorConverterBase Operator, int ComponentCount) converters = |
|
||||
this.ColorModel switch |
|
||||
{ |
|
||||
JpegColorModel.Grayscale => ( |
|
||||
new JpegColorConverterBase.GrayScaleVector512(8), |
|
||||
new JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.GrayScaleOperator>(8), |
|
||||
1), |
|
||||
JpegColorModel.Rgb => ( |
|
||||
new JpegColorConverterBase.RgbVector512(8), |
|
||||
new JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.RgbOperator>(8), |
|
||||
3), |
|
||||
JpegColorModel.Cmyk => ( |
|
||||
new JpegColorConverterBase.CmykVector512(8), |
|
||||
new JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.CmykOperator>(8), |
|
||||
4), |
|
||||
JpegColorModel.YCbCr => ( |
|
||||
new JpegColorConverterBase.YCbCrVector512(8), |
|
||||
new JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.YCbCrOperator>(8), |
|
||||
3), |
|
||||
JpegColorModel.YccK => ( |
|
||||
new JpegColorConverterBase.YccKVector512(8), |
|
||||
new JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.YccKOperator>(8), |
|
||||
4), |
|
||||
JpegColorModel.TiffCmyk => ( |
|
||||
new JpegColorConverterBase.TiffCmykVector512(8), |
|
||||
new JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.TiffCmykOperator>(8), |
|
||||
4), |
|
||||
JpegColorModel.TiffYccK => ( |
|
||||
new JpegColorConverterBase.TiffYccKVector512(8), |
|
||||
new JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.TiffYccKOperator>(8), |
|
||||
4), |
|
||||
_ => throw new InvalidOperationException(), |
|
||||
}; |
|
||||
|
|
||||
(this.legacy, this.operatorConverter, this.componentCount) = converters; |
|
||||
|
|
||||
Random random = new(42); |
|
||||
this.legacyC0 = CreateRandomValues(this.Count, random); |
|
||||
this.legacyC1 = CreateRandomValues(this.Count, random); |
|
||||
this.legacyC2 = CreateRandomValues(this.Count, random); |
|
||||
this.legacyC3 = CreateRandomValues(this.Count, random); |
|
||||
this.operatorC0 = this.legacyC0.ToArray(); |
|
||||
this.operatorC1 = this.legacyC1.ToArray(); |
|
||||
this.operatorC2 = this.legacyC2.ToArray(); |
|
||||
this.operatorC3 = this.legacyC3.ToArray(); |
|
||||
this.r = CreateRandomValues(this.Count, random); |
|
||||
this.g = CreateRandomValues(this.Count, random); |
|
||||
this.b = CreateRandomValues(this.Count, random); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts JPEG components to RGB using the replaced Vector512 implementation.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("ToRgb")] |
|
||||
public void LegacyToRgb() |
|
||||
{ |
|
||||
JpegColorConverterBase.ComponentValues values = this.CreateLegacyValues(); |
|
||||
|
|
||||
this.legacy.ConvertToRgbInPlace(values); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts JPEG components to RGB using the shared operator traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("ToRgb")] |
|
||||
public void OperatorToRgb() |
|
||||
{ |
|
||||
JpegColorConverterBase.ComponentValues values = this.CreateOperatorValues(); |
|
||||
|
|
||||
this.operatorConverter.ConvertToRgbInPlace(values); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts RGB to JPEG components using the replaced Vector512 implementation.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("FromRgb")] |
|
||||
public void LegacyFromRgb() |
|
||||
{ |
|
||||
JpegColorConverterBase.ComponentValues values = this.CreateLegacyValues(); |
|
||||
|
|
||||
this.legacy.ConvertFromRgb(values, this.r, this.g, this.b); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts RGB to JPEG components using the shared operator traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("FromRgb")] |
|
||||
public void OperatorFromRgb() |
|
||||
{ |
|
||||
JpegColorConverterBase.ComponentValues values = this.CreateOperatorValues(); |
|
||||
|
|
||||
this.operatorConverter.ConvertFromRgb(values, this.r, this.g, this.b); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a component view over the buffers owned by the legacy converter.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The component view for the configured color model.</returns>
|
|
||||
private JpegColorConverterBase.ComponentValues CreateLegacyValues() |
|
||||
=> new( |
|
||||
this.componentCount, |
|
||||
this.legacyC0, |
|
||||
this.componentCount > 1 ? this.legacyC1 : this.legacyC0, |
|
||||
this.componentCount > 2 ? this.legacyC2 : this.legacyC0, |
|
||||
this.componentCount > 3 ? this.legacyC3 : []); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a component view over the buffers owned by the operator converter.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The component view for the configured color model.</returns>
|
|
||||
private JpegColorConverterBase.ComponentValues CreateOperatorValues() |
|
||||
=> new( |
|
||||
this.componentCount, |
|
||||
this.operatorC0, |
|
||||
this.componentCount > 1 ? this.operatorC1 : this.operatorC0, |
|
||||
this.componentCount > 2 ? this.operatorC2 : this.operatorC0, |
|
||||
this.componentCount > 3 ? this.operatorC3 : []); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates deterministic sample-domain values for one component plane.
|
|
||||
/// </summary>
|
|
||||
/// <param name="length">The number of samples to create.</param>
|
|
||||
/// <param name="random">The deterministic random source shared by setup.</param>
|
|
||||
/// <returns>The populated component plane.</returns>
|
|
||||
private static float[] CreateRandomValues(int length, Random random) |
|
||||
{ |
|
||||
float[] values = new float[length]; |
|
||||
|
|
||||
for (int i = 0; i < values.Length; i++) |
|
||||
{ |
|
||||
values[i] = (float)random.NextDouble() * 255F; |
|
||||
} |
|
||||
|
|
||||
return values; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the JPEG color model used by a benchmark case.
|
|
||||
/// </summary>
|
|
||||
public enum JpegColorModel |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// One luminance component.
|
|
||||
/// </summary>
|
|
||||
Grayscale, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Three direct RGB components.
|
|
||||
/// </summary>
|
|
||||
Rgb, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Four inverted Adobe CMYK components.
|
|
||||
/// </summary>
|
|
||||
Cmyk, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Three JPEG YCbCr components.
|
|
||||
/// </summary>
|
|
||||
YCbCr, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Four inverted Adobe YCCK components.
|
|
||||
/// </summary>
|
|
||||
YccK, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Four non-inverted TIFF CMYK components.
|
|
||||
/// </summary>
|
|
||||
TiffCmyk, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Four non-inverted TIFF YCCK components.
|
|
||||
/// </summary>
|
|
||||
TiffYccK, |
|
||||
} |
|
||||
} |
|
||||
@ -1,325 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Runtime.CompilerServices; |
|
||||
using BenchmarkDotNet.Attributes; |
|
||||
using BenchmarkDotNet.Columns; |
|
||||
using BenchmarkDotNet.Configs; |
|
||||
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Exposes every closed JPEG operator traversal beside the Vector512 implementation it replaces.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// A 63-pixel buffer leaves 256-bit, 128-bit, and scalar remainders after the 512-bit loop, making
|
|
||||
/// every operator overload visible in the generated traversal assembly on AVX-512 hardware.
|
|
||||
/// </remarks>
|
|
||||
[Config(typeof(Config.Analysis))] |
|
||||
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] |
|
||||
[CategoriesColumn] |
|
||||
public class JpegColorConverterTraversalAssembly |
|
||||
{ |
|
||||
private const int Count = 63; |
|
||||
|
|
||||
private readonly JpegColorConverterBase.GrayScaleVector512 grayscaleLegacy = new(8); |
|
||||
private readonly JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.GrayScaleOperator> grayscaleOperator = new(8); |
|
||||
private readonly JpegColorConverterBase.RgbVector512 rgbLegacy = new(8); |
|
||||
private readonly JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.RgbOperator> rgbOperator = new(8); |
|
||||
private readonly JpegColorConverterBase.CmykVector512 cmykLegacy = new(8); |
|
||||
private readonly JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.CmykOperator> cmykOperator = new(8); |
|
||||
private readonly JpegColorConverterBase.YCbCrVector512 yCbCrLegacy = new(8); |
|
||||
private readonly JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.YCbCrOperator> yCbCrOperator = new(8); |
|
||||
private readonly JpegColorConverterBase.YccKVector512 yccKLegacy = new(8); |
|
||||
private readonly JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.YccKOperator> yccKOperator = new(8); |
|
||||
private readonly JpegColorConverterBase.TiffCmykVector512 tiffCmykLegacy = new(8); |
|
||||
private readonly JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.TiffCmykOperator> tiffCmykOperator = new(8); |
|
||||
private readonly JpegColorConverterBase.TiffYccKVector512 tiffYccKLegacy = new(8); |
|
||||
private readonly JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.TiffYccKOperator> tiffYccKOperator = new(8); |
|
||||
|
|
||||
private readonly float[] legacyC0 = new float[Count]; |
|
||||
private readonly float[] legacyC1 = new float[Count]; |
|
||||
private readonly float[] legacyC2 = new float[Count]; |
|
||||
private readonly float[] legacyC3 = new float[Count]; |
|
||||
private readonly float[] operatorC0 = new float[Count]; |
|
||||
private readonly float[] operatorC1 = new float[Count]; |
|
||||
private readonly float[] operatorC2 = new float[Count]; |
|
||||
private readonly float[] operatorC3 = new float[Count]; |
|
||||
private readonly float[] r = new float[Count]; |
|
||||
private readonly float[] g = new float[Count]; |
|
||||
private readonly float[] b = new float[Count]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Populates the component and RGB planes with deterministic sample-domain values.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
Random random = new(42); |
|
||||
|
|
||||
for (int i = 0; i < Count; i++) |
|
||||
{ |
|
||||
// Independent non-constant lanes prevent the JIT from folding arithmetic or mask decisions.
|
|
||||
this.legacyC0[i] = this.operatorC0[i] = (float)random.NextDouble() * 255F; |
|
||||
this.legacyC1[i] = this.operatorC1[i] = (float)random.NextDouble() * 255F; |
|
||||
this.legacyC2[i] = this.operatorC2[i] = (float)random.NextDouble() * 255F; |
|
||||
this.legacyC3[i] = this.operatorC3[i] = (float)random.NextDouble() * 255F; |
|
||||
this.r[i] = (float)random.NextDouble() * 255F; |
|
||||
this.g[i] = (float)random.NextDouble() * 255F; |
|
||||
this.b[i] = (float)random.NextDouble() * 255F; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced grayscale component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("Grayscale.ToRgb")] |
|
||||
public void GrayscaleLegacyToRgb() |
|
||||
=> this.grayscaleLegacy.ConvertToRgbInPlace(this.CreateLegacyValues(1)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared grayscale component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("Grayscale.ToRgb")] |
|
||||
public void GrayscaleOperatorToRgb() |
|
||||
=> this.grayscaleOperator.ConvertToRgbInPlace(this.CreateOperatorValues(1)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced grayscale RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("Grayscale.FromRgb")] |
|
||||
public void GrayscaleLegacyFromRgb() |
|
||||
=> this.grayscaleLegacy.ConvertFromRgb(this.CreateLegacyValues(1), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared grayscale RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("Grayscale.FromRgb")] |
|
||||
public void GrayscaleOperatorFromRgb() |
|
||||
=> this.grayscaleOperator.ConvertFromRgb(this.CreateOperatorValues(1), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced RGB component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("Rgb.ToRgb")] |
|
||||
public void RgbLegacyToRgb() |
|
||||
=> this.rgbLegacy.ConvertToRgbInPlace(this.CreateLegacyValues(3)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared RGB component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("Rgb.ToRgb")] |
|
||||
public void RgbOperatorToRgb() |
|
||||
=> this.rgbOperator.ConvertToRgbInPlace(this.CreateOperatorValues(3)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced RGB RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("Rgb.FromRgb")] |
|
||||
public void RgbLegacyFromRgb() |
|
||||
=> this.rgbLegacy.ConvertFromRgb(this.CreateLegacyValues(3), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared RGB RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("Rgb.FromRgb")] |
|
||||
public void RgbOperatorFromRgb() |
|
||||
=> this.rgbOperator.ConvertFromRgb(this.CreateOperatorValues(3), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced CMYK component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("Cmyk.ToRgb")] |
|
||||
public void CmykLegacyToRgb() |
|
||||
=> this.cmykLegacy.ConvertToRgbInPlace(this.CreateLegacyValues(4)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared CMYK component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("Cmyk.ToRgb")] |
|
||||
public void CmykOperatorToRgb() |
|
||||
=> this.cmykOperator.ConvertToRgbInPlace(this.CreateOperatorValues(4)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced CMYK RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("Cmyk.FromRgb")] |
|
||||
public void CmykLegacyFromRgb() |
|
||||
=> this.cmykLegacy.ConvertFromRgb(this.CreateLegacyValues(4), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared CMYK RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("Cmyk.FromRgb")] |
|
||||
public void CmykOperatorFromRgb() |
|
||||
=> this.cmykOperator.ConvertFromRgb(this.CreateOperatorValues(4), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced YCbCr component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("YCbCr.ToRgb")] |
|
||||
public void YCbCrLegacyToRgb() |
|
||||
=> this.yCbCrLegacy.ConvertToRgbInPlace(this.CreateLegacyValues(3)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared YCbCr component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("YCbCr.ToRgb")] |
|
||||
public void YCbCrOperatorToRgb() |
|
||||
=> this.yCbCrOperator.ConvertToRgbInPlace(this.CreateOperatorValues(3)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced YCbCr RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("YCbCr.FromRgb")] |
|
||||
public void YCbCrLegacyFromRgb() |
|
||||
=> this.yCbCrLegacy.ConvertFromRgb(this.CreateLegacyValues(3), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared YCbCr RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("YCbCr.FromRgb")] |
|
||||
public void YCbCrOperatorFromRgb() |
|
||||
=> this.yCbCrOperator.ConvertFromRgb(this.CreateOperatorValues(3), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced YCCK component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("YccK.ToRgb")] |
|
||||
public void YccKLegacyToRgb() |
|
||||
=> this.yccKLegacy.ConvertToRgbInPlace(this.CreateLegacyValues(4)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared YCCK component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("YccK.ToRgb")] |
|
||||
public void YccKOperatorToRgb() |
|
||||
=> this.yccKOperator.ConvertToRgbInPlace(this.CreateOperatorValues(4)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced YCCK RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("YccK.FromRgb")] |
|
||||
public void YccKLegacyFromRgb() |
|
||||
=> this.yccKLegacy.ConvertFromRgb(this.CreateLegacyValues(4), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared YCCK RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("YccK.FromRgb")] |
|
||||
public void YccKOperatorFromRgb() |
|
||||
=> this.yccKOperator.ConvertFromRgb(this.CreateOperatorValues(4), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced TIFF CMYK component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("TiffCmyk.ToRgb")] |
|
||||
public void TiffCmykLegacyToRgb() |
|
||||
=> this.tiffCmykLegacy.ConvertToRgbInPlace(this.CreateLegacyValues(4)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared TIFF CMYK component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("TiffCmyk.ToRgb")] |
|
||||
public void TiffCmykOperatorToRgb() |
|
||||
=> this.tiffCmykOperator.ConvertToRgbInPlace(this.CreateOperatorValues(4)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced TIFF CMYK RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("TiffCmyk.FromRgb")] |
|
||||
public void TiffCmykLegacyFromRgb() |
|
||||
=> this.tiffCmykLegacy.ConvertFromRgb(this.CreateLegacyValues(4), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared TIFF CMYK RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("TiffCmyk.FromRgb")] |
|
||||
public void TiffCmykOperatorFromRgb() |
|
||||
=> this.tiffCmykOperator.ConvertFromRgb(this.CreateOperatorValues(4), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced TIFF YCCK component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("TiffYccK.ToRgb")] |
|
||||
public void TiffYccKLegacyToRgb() |
|
||||
=> this.tiffYccKLegacy.ConvertToRgbInPlace(this.CreateLegacyValues(4)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared TIFF YCCK component-to-RGB traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("TiffYccK.ToRgb")] |
|
||||
public void TiffYccKOperatorToRgb() |
|
||||
=> this.tiffYccKOperator.ConvertToRgbInPlace(this.CreateOperatorValues(4)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the replaced TIFF YCCK RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("TiffYccK.FromRgb")] |
|
||||
public void TiffYccKLegacyFromRgb() |
|
||||
=> this.tiffYccKLegacy.ConvertFromRgb(this.CreateLegacyValues(4), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Runs the shared TIFF YCCK RGB-to-component traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("TiffYccK.FromRgb")] |
|
||||
public void TiffYccKOperatorFromRgb() |
|
||||
=> this.tiffYccKOperator.ConvertFromRgb(this.CreateOperatorValues(4), this.r, this.g, this.b); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a correctly aliased component view over the legacy planes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="componentCount">The number of component planes owned by the color model.</param>
|
|
||||
/// <returns>The legacy component view.</returns>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private JpegColorConverterBase.ComponentValues CreateLegacyValues(int componentCount) |
|
||||
=> new( |
|
||||
componentCount, |
|
||||
this.legacyC0, |
|
||||
componentCount > 1 ? this.legacyC1 : this.legacyC0, |
|
||||
componentCount > 2 ? this.legacyC2 : this.legacyC0, |
|
||||
componentCount > 3 ? this.legacyC3 : []); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a correctly aliased component view over the operator planes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="componentCount">The number of component planes owned by the color model.</param>
|
|
||||
/// <returns>The operator component view.</returns>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private JpegColorConverterBase.ComponentValues CreateOperatorValues(int componentCount) |
|
||||
=> new( |
|
||||
componentCount, |
|
||||
this.operatorC0, |
|
||||
componentCount > 1 ? this.operatorC1 : this.operatorC0, |
|
||||
componentCount > 2 ? this.operatorC2 : this.operatorC0, |
|
||||
componentCount > 3 ? this.operatorC3 : []); |
|
||||
} |
|
||||
@ -1,127 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using BenchmarkDotNet.Attributes; |
|
||||
using BenchmarkDotNet.Columns; |
|
||||
using BenchmarkDotNet.Configs; |
|
||||
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Compares the shared YCbCr operator traversal with the Vector512 implementation it replaces.
|
|
||||
/// </summary>
|
|
||||
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] |
|
||||
[CategoriesColumn] |
|
||||
public class YCbCrOperatorComparison |
|
||||
{ |
|
||||
private JpegColorConverterBase.YCbCrVector512 legacy; |
|
||||
private JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.YCbCrOperator> operatorConverter; |
|
||||
private float[] legacyC0; |
|
||||
private float[] legacyC1; |
|
||||
private float[] legacyC2; |
|
||||
private float[] operatorC0; |
|
||||
private float[] operatorC1; |
|
||||
private float[] operatorC2; |
|
||||
private float[] r; |
|
||||
private float[] g; |
|
||||
private float[] b; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the number of pixels converted by each invocation.
|
|
||||
/// </summary>
|
|
||||
[Params(8, 128, 1024)] |
|
||||
public int Count { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates equivalent converter inputs in independent component buffers.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.legacy = new JpegColorConverterBase.YCbCrVector512(8); |
|
||||
this.operatorConverter = |
|
||||
new JpegColorConverterBase.JpegColorConverter<JpegColorConverterBase.YCbCrOperator>(8); |
|
||||
|
|
||||
Random random = new(42); |
|
||||
this.legacyC0 = CreateRandomValues(this.Count, random); |
|
||||
this.legacyC1 = CreateRandomValues(this.Count, random); |
|
||||
this.legacyC2 = CreateRandomValues(this.Count, random); |
|
||||
this.operatorC0 = this.legacyC0.ToArray(); |
|
||||
this.operatorC1 = this.legacyC1.ToArray(); |
|
||||
this.operatorC2 = this.legacyC2.ToArray(); |
|
||||
this.r = CreateRandomValues(this.Count, random); |
|
||||
this.g = CreateRandomValues(this.Count, random); |
|
||||
this.b = CreateRandomValues(this.Count, random); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts YCbCr components to RGB using the Vector512 implementation.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("ToRgb")] |
|
||||
public void LegacyToRgb() |
|
||||
{ |
|
||||
JpegColorConverterBase.ComponentValues values = |
|
||||
new(3, this.legacyC0, this.legacyC1, this.legacyC2, []); |
|
||||
|
|
||||
this.legacy.ConvertToRgbInPlace(values); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts YCbCr components to RGB using the shared operator traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("ToRgb")] |
|
||||
public void OperatorToRgb() |
|
||||
{ |
|
||||
JpegColorConverterBase.ComponentValues values = |
|
||||
new(3, this.operatorC0, this.operatorC1, this.operatorC2, []); |
|
||||
|
|
||||
this.operatorConverter.ConvertToRgbInPlace(values); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts RGB to YCbCr components using the Vector512 implementation.
|
|
||||
/// </summary>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
[BenchmarkCategory("FromRgb")] |
|
||||
public void LegacyFromRgb() |
|
||||
{ |
|
||||
JpegColorConverterBase.ComponentValues values = |
|
||||
new(3, this.legacyC0, this.legacyC1, this.legacyC2, []); |
|
||||
|
|
||||
this.legacy.ConvertFromRgb(values, this.r, this.g, this.b); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts RGB to YCbCr components using the shared operator traversal.
|
|
||||
/// </summary>
|
|
||||
[Benchmark] |
|
||||
[BenchmarkCategory("FromRgb")] |
|
||||
public void OperatorFromRgb() |
|
||||
{ |
|
||||
JpegColorConverterBase.ComponentValues values = |
|
||||
new(3, this.operatorC0, this.operatorC1, this.operatorC2, []); |
|
||||
|
|
||||
this.operatorConverter.ConvertFromRgb(values, this.r, this.g, this.b); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates deterministic sample-domain values for one component plane.
|
|
||||
/// </summary>
|
|
||||
/// <param name="length">The number of samples to create.</param>
|
|
||||
/// <param name="random">The deterministic random source shared by setup.</param>
|
|
||||
/// <returns>The populated component plane.</returns>
|
|
||||
private static float[] CreateRandomValues(int length, Random random) |
|
||||
{ |
|
||||
float[] values = new float[length]; |
|
||||
|
|
||||
for (int i = 0; i < values.Length; i++) |
|
||||
{ |
|
||||
values[i] = (float)random.NextDouble() * 255F; |
|
||||
} |
|
||||
|
|
||||
return values; |
|
||||
} |
|
||||
} |
|
||||
@ -1,470 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using System.Runtime.Intrinsics; |
|
||||
using System.Runtime.Intrinsics.X86; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Png; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Retains the filter-specific PNG encode traversals for direct performance comparison.
|
|
||||
/// </summary>
|
|
||||
internal static class PngFilterEncodeBaseline |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Executes the filter-specific Sub traversal.
|
|
||||
/// </summary>
|
|
||||
public static void EncodeSub( |
|
||||
ReadOnlySpan<byte> scanline, |
|
||||
Span<byte> result, |
|
||||
int bytesPerPixel, |
|
||||
out int sum) |
|
||||
{ |
|
||||
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); |
|
||||
ref byte resultBaseRef = ref MemoryMarshal.GetReference(result); |
|
||||
sum = 0; |
|
||||
resultBaseRef = 1; |
|
||||
|
|
||||
nuint x = 0; |
|
||||
|
|
||||
for (; x < (uint)bytesPerPixel;) |
|
||||
{ |
|
||||
byte scan = Unsafe.Add(ref scanBaseRef, x); |
|
||||
x++; |
|
||||
ref byte residual = ref Unsafe.Add(ref resultBaseRef, x); |
|
||||
residual = scan; |
|
||||
sum += Numerics.Abs(unchecked((sbyte)residual)); |
|
||||
} |
|
||||
|
|
||||
if (Avx2.IsSupported) |
|
||||
{ |
|
||||
Vector256<byte> zero = Vector256<byte>.Zero; |
|
||||
Vector256<int> accumulator = Vector256<int>.Zero; |
|
||||
|
|
||||
for (nuint xLeft = x - (uint)bytesPerPixel; (int)x <= scanline.Length - Vector256<byte>.Count; xLeft += (uint)Vector256<byte>.Count) |
|
||||
{ |
|
||||
Vector256<byte> scan = Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref scanBaseRef, x)); |
|
||||
Vector256<byte> left = Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref scanBaseRef, xLeft)); |
|
||||
Vector256<byte> residual = Avx2.Subtract(scan, left); |
|
||||
|
|
||||
Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = residual; |
|
||||
x += (uint)Vector256<byte>.Count; |
|
||||
accumulator = Avx2.Add( |
|
||||
accumulator, |
|
||||
Avx2.SumAbsoluteDifferences(Avx2.Abs(residual.AsSByte()), zero).AsInt32()); |
|
||||
} |
|
||||
|
|
||||
sum += Numerics.EvenReduceSum(accumulator); |
|
||||
} |
|
||||
else if (Vector.IsHardwareAccelerated) |
|
||||
{ |
|
||||
Vector<uint> accumulator = Vector<uint>.Zero; |
|
||||
|
|
||||
for (nuint xLeft = x - (uint)bytesPerPixel; (int)x <= scanline.Length - Vector<byte>.Count; xLeft += (uint)Vector<byte>.Count) |
|
||||
{ |
|
||||
Vector<byte> scan = Unsafe.As<byte, Vector<byte>>(ref Unsafe.Add(ref scanBaseRef, x)); |
|
||||
Vector<byte> left = Unsafe.As<byte, Vector<byte>>(ref Unsafe.Add(ref scanBaseRef, xLeft)); |
|
||||
Vector<byte> residual = scan - left; |
|
||||
|
|
||||
Unsafe.As<byte, Vector<byte>>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = residual; |
|
||||
x += (uint)Vector<byte>.Count; |
|
||||
Numerics.Accumulate( |
|
||||
ref accumulator, |
|
||||
Vector.AsVectorByte(Vector.Abs(Vector.AsVectorSByte(residual)))); |
|
||||
} |
|
||||
|
|
||||
for (int i = 0; i < Vector<uint>.Count; i++) |
|
||||
{ |
|
||||
sum += (int)accumulator[i]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
for (nuint xLeft = x - (uint)bytesPerPixel; x < (uint)scanline.Length; xLeft++) |
|
||||
{ |
|
||||
byte scan = Unsafe.Add(ref scanBaseRef, x); |
|
||||
byte left = Unsafe.Add(ref scanBaseRef, xLeft); |
|
||||
x++; |
|
||||
ref byte residual = ref Unsafe.Add(ref resultBaseRef, x); |
|
||||
residual = (byte)(scan - left); |
|
||||
sum += Numerics.Abs(unchecked((sbyte)residual)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Executes the filter-specific Up traversal.
|
|
||||
/// </summary>
|
|
||||
public static void EncodeUp( |
|
||||
ReadOnlySpan<byte> scanline, |
|
||||
ReadOnlySpan<byte> previousScanline, |
|
||||
Span<byte> result, |
|
||||
out int sum) |
|
||||
{ |
|
||||
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); |
|
||||
ref byte previousBaseRef = ref MemoryMarshal.GetReference(previousScanline); |
|
||||
ref byte resultBaseRef = ref MemoryMarshal.GetReference(result); |
|
||||
sum = 0; |
|
||||
resultBaseRef = 2; |
|
||||
|
|
||||
nuint x = 0; |
|
||||
|
|
||||
if (Avx2.IsSupported) |
|
||||
{ |
|
||||
Vector256<byte> zero = Vector256<byte>.Zero; |
|
||||
Vector256<int> accumulator = Vector256<int>.Zero; |
|
||||
|
|
||||
for (; (int)x <= scanline.Length - Vector256<byte>.Count;) |
|
||||
{ |
|
||||
Vector256<byte> scan = Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref scanBaseRef, x)); |
|
||||
Vector256<byte> above = Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref previousBaseRef, x)); |
|
||||
Vector256<byte> residual = Avx2.Subtract(scan, above); |
|
||||
|
|
||||
Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = residual; |
|
||||
x += (uint)Vector256<byte>.Count; |
|
||||
accumulator = Avx2.Add( |
|
||||
accumulator, |
|
||||
Avx2.SumAbsoluteDifferences(Avx2.Abs(residual.AsSByte()), zero).AsInt32()); |
|
||||
} |
|
||||
|
|
||||
sum += Numerics.EvenReduceSum(accumulator); |
|
||||
} |
|
||||
else if (Vector.IsHardwareAccelerated) |
|
||||
{ |
|
||||
Vector<uint> accumulator = Vector<uint>.Zero; |
|
||||
|
|
||||
for (; (int)x <= scanline.Length - Vector<byte>.Count;) |
|
||||
{ |
|
||||
Vector<byte> scan = Unsafe.As<byte, Vector<byte>>(ref Unsafe.Add(ref scanBaseRef, x)); |
|
||||
Vector<byte> above = Unsafe.As<byte, Vector<byte>>(ref Unsafe.Add(ref previousBaseRef, x)); |
|
||||
Vector<byte> residual = scan - above; |
|
||||
|
|
||||
Unsafe.As<byte, Vector<byte>>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = residual; |
|
||||
x += (uint)Vector<byte>.Count; |
|
||||
Numerics.Accumulate( |
|
||||
ref accumulator, |
|
||||
Vector.AsVectorByte(Vector.Abs(Vector.AsVectorSByte(residual)))); |
|
||||
} |
|
||||
|
|
||||
for (int i = 0; i < Vector<uint>.Count; i++) |
|
||||
{ |
|
||||
sum += (int)accumulator[i]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
for (; x < (uint)scanline.Length;) |
|
||||
{ |
|
||||
byte scan = Unsafe.Add(ref scanBaseRef, x); |
|
||||
byte above = Unsafe.Add(ref previousBaseRef, x); |
|
||||
x++; |
|
||||
ref byte residual = ref Unsafe.Add(ref resultBaseRef, x); |
|
||||
residual = (byte)(scan - above); |
|
||||
sum += Numerics.Abs(unchecked((sbyte)residual)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Executes the filter-specific Average traversal.
|
|
||||
/// </summary>
|
|
||||
public static void EncodeAverage( |
|
||||
ReadOnlySpan<byte> scanline, |
|
||||
ReadOnlySpan<byte> previousScanline, |
|
||||
Span<byte> result, |
|
||||
uint bytesPerPixel, |
|
||||
out int sum) |
|
||||
{ |
|
||||
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); |
|
||||
ref byte previousBaseRef = ref MemoryMarshal.GetReference(previousScanline); |
|
||||
ref byte resultBaseRef = ref MemoryMarshal.GetReference(result); |
|
||||
sum = 0; |
|
||||
resultBaseRef = 3; |
|
||||
|
|
||||
nuint x = 0; |
|
||||
|
|
||||
for (; x < bytesPerPixel;) |
|
||||
{ |
|
||||
byte scan = Unsafe.Add(ref scanBaseRef, x); |
|
||||
byte above = Unsafe.Add(ref previousBaseRef, x); |
|
||||
x++; |
|
||||
ref byte residual = ref Unsafe.Add(ref resultBaseRef, x); |
|
||||
residual = (byte)(scan - (above >> 1)); |
|
||||
sum += Numerics.Abs(unchecked((sbyte)residual)); |
|
||||
} |
|
||||
|
|
||||
if (Avx2.IsSupported) |
|
||||
{ |
|
||||
Vector256<byte> zero = Vector256<byte>.Zero; |
|
||||
Vector256<int> accumulator = Vector256<int>.Zero; |
|
||||
Vector256<byte> allBitsSet = Avx2.CompareEqual(accumulator, accumulator).AsByte(); |
|
||||
|
|
||||
for (nuint xLeft = x - bytesPerPixel; (int)x <= scanline.Length - Vector256<byte>.Count; xLeft += (uint)Vector256<byte>.Count) |
|
||||
{ |
|
||||
Vector256<byte> scan = Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref scanBaseRef, x)); |
|
||||
Vector256<byte> left = Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref scanBaseRef, xLeft)); |
|
||||
Vector256<byte> above = Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref previousBaseRef, x)); |
|
||||
Vector256<byte> average = Avx2.Xor( |
|
||||
Avx2.Average(Avx2.Xor(left, allBitsSet), Avx2.Xor(above, allBitsSet)), |
|
||||
allBitsSet); |
|
||||
|
|
||||
Vector256<byte> residual = Avx2.Subtract(scan, average); |
|
||||
Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = residual; |
|
||||
x += (uint)Vector256<byte>.Count; |
|
||||
accumulator = Avx2.Add( |
|
||||
accumulator, |
|
||||
Avx2.SumAbsoluteDifferences(Avx2.Abs(residual.AsSByte()), zero).AsInt32()); |
|
||||
} |
|
||||
|
|
||||
sum += Numerics.EvenReduceSum(accumulator); |
|
||||
} |
|
||||
else if (Sse2.IsSupported) |
|
||||
{ |
|
||||
Vector128<byte> zero = Vector128<byte>.Zero; |
|
||||
Vector128<int> accumulator = Vector128<int>.Zero; |
|
||||
Vector128<byte> allBitsSet = Sse2.CompareEqual(accumulator, accumulator).AsByte(); |
|
||||
|
|
||||
for (nuint xLeft = x - bytesPerPixel; (int)x <= scanline.Length - Vector128<byte>.Count; xLeft += (uint)Vector128<byte>.Count) |
|
||||
{ |
|
||||
Vector128<byte> scan = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref scanBaseRef, x)); |
|
||||
Vector128<byte> left = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref scanBaseRef, xLeft)); |
|
||||
Vector128<byte> above = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref previousBaseRef, x)); |
|
||||
Vector128<byte> average = Sse2.Xor( |
|
||||
Sse2.Average(Sse2.Xor(left, allBitsSet), Sse2.Xor(above, allBitsSet)), |
|
||||
allBitsSet); |
|
||||
|
|
||||
Vector128<byte> residual = Sse2.Subtract(scan, average); |
|
||||
Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = residual; |
|
||||
x += (uint)Vector128<byte>.Count; |
|
||||
|
|
||||
Vector128<byte> absolute; |
|
||||
|
|
||||
if (Ssse3.IsSupported) |
|
||||
{ |
|
||||
absolute = Ssse3.Abs(residual.AsSByte()); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
Vector128<sbyte> mask = Sse2.CompareGreaterThan(zero.AsSByte(), residual.AsSByte()); |
|
||||
absolute = Sse2.Xor(Sse2.Add(residual.AsSByte(), mask), mask).AsByte(); |
|
||||
} |
|
||||
|
|
||||
accumulator = Sse2.Add( |
|
||||
accumulator, |
|
||||
Sse2.SumAbsoluteDifferences(absolute, zero).AsInt32()); |
|
||||
} |
|
||||
|
|
||||
sum += Numerics.EvenReduceSum(accumulator); |
|
||||
} |
|
||||
|
|
||||
for (nuint xLeft = x - bytesPerPixel; x < (uint)scanline.Length; xLeft++) |
|
||||
{ |
|
||||
byte scan = Unsafe.Add(ref scanBaseRef, x); |
|
||||
byte left = Unsafe.Add(ref scanBaseRef, xLeft); |
|
||||
byte above = Unsafe.Add(ref previousBaseRef, x); |
|
||||
x++; |
|
||||
ref byte residual = ref Unsafe.Add(ref resultBaseRef, x); |
|
||||
residual = (byte)(scan - ((left + above) >> 1)); |
|
||||
sum += Numerics.Abs(unchecked((sbyte)residual)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Executes the filter-specific Paeth traversal.
|
|
||||
/// </summary>
|
|
||||
public static void EncodePaeth( |
|
||||
ReadOnlySpan<byte> scanline, |
|
||||
ReadOnlySpan<byte> previousScanline, |
|
||||
Span<byte> result, |
|
||||
int bytesPerPixel, |
|
||||
out int sum) |
|
||||
{ |
|
||||
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); |
|
||||
ref byte previousBaseRef = ref MemoryMarshal.GetReference(previousScanline); |
|
||||
ref byte resultBaseRef = ref MemoryMarshal.GetReference(result); |
|
||||
sum = 0; |
|
||||
resultBaseRef = 4; |
|
||||
|
|
||||
nuint x = 0; |
|
||||
|
|
||||
for (; x < (uint)bytesPerPixel;) |
|
||||
{ |
|
||||
byte scan = Unsafe.Add(ref scanBaseRef, x); |
|
||||
byte above = Unsafe.Add(ref previousBaseRef, x); |
|
||||
x++; |
|
||||
ref byte residual = ref Unsafe.Add(ref resultBaseRef, x); |
|
||||
residual = (byte)(scan - above); |
|
||||
sum += Numerics.Abs(unchecked((sbyte)residual)); |
|
||||
} |
|
||||
|
|
||||
if (Avx2.IsSupported) |
|
||||
{ |
|
||||
Vector256<byte> zero = Vector256<byte>.Zero; |
|
||||
Vector256<int> accumulator = Vector256<int>.Zero; |
|
||||
|
|
||||
for (nuint xLeft = x - (uint)bytesPerPixel; (int)x <= scanline.Length - Vector256<byte>.Count; xLeft += (uint)Vector256<byte>.Count) |
|
||||
{ |
|
||||
Vector256<byte> scan = Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref scanBaseRef, x)); |
|
||||
Vector256<byte> left = Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref scanBaseRef, xLeft)); |
|
||||
Vector256<byte> above = Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref previousBaseRef, x)); |
|
||||
Vector256<byte> upperLeft = Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref previousBaseRef, xLeft)); |
|
||||
Vector256<byte> residual = Avx2.Subtract(scan, PaethPredictor(left, above, upperLeft)); |
|
||||
|
|
||||
Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = residual; |
|
||||
x += (uint)Vector256<byte>.Count; |
|
||||
accumulator = Avx2.Add( |
|
||||
accumulator, |
|
||||
Avx2.SumAbsoluteDifferences(Avx2.Abs(residual.AsSByte()), zero).AsInt32()); |
|
||||
} |
|
||||
|
|
||||
sum += Numerics.EvenReduceSum(accumulator); |
|
||||
} |
|
||||
else if (Vector.IsHardwareAccelerated) |
|
||||
{ |
|
||||
Vector<uint> accumulator = Vector<uint>.Zero; |
|
||||
|
|
||||
for (nuint xLeft = x - (uint)bytesPerPixel; (int)x <= scanline.Length - Vector<byte>.Count; xLeft += (uint)Vector<byte>.Count) |
|
||||
{ |
|
||||
Vector<byte> scan = Unsafe.As<byte, Vector<byte>>(ref Unsafe.Add(ref scanBaseRef, x)); |
|
||||
Vector<byte> left = Unsafe.As<byte, Vector<byte>>(ref Unsafe.Add(ref scanBaseRef, xLeft)); |
|
||||
Vector<byte> above = Unsafe.As<byte, Vector<byte>>(ref Unsafe.Add(ref previousBaseRef, x)); |
|
||||
Vector<byte> upperLeft = Unsafe.As<byte, Vector<byte>>(ref Unsafe.Add(ref previousBaseRef, xLeft)); |
|
||||
Vector<byte> residual = scan - PaethPredictor(left, above, upperLeft); |
|
||||
|
|
||||
Unsafe.As<byte, Vector<byte>>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = residual; |
|
||||
x += (uint)Vector<byte>.Count; |
|
||||
Numerics.Accumulate( |
|
||||
ref accumulator, |
|
||||
Vector.AsVectorByte(Vector.Abs(Vector.AsVectorSByte(residual)))); |
|
||||
} |
|
||||
|
|
||||
for (int i = 0; i < Vector<uint>.Count; i++) |
|
||||
{ |
|
||||
sum += (int)accumulator[i]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
for (nuint xLeft = x - (uint)bytesPerPixel; x < (uint)scanline.Length; xLeft++) |
|
||||
{ |
|
||||
byte scan = Unsafe.Add(ref scanBaseRef, x); |
|
||||
byte left = Unsafe.Add(ref scanBaseRef, xLeft); |
|
||||
byte above = Unsafe.Add(ref previousBaseRef, x); |
|
||||
byte upperLeft = Unsafe.Add(ref previousBaseRef, xLeft); |
|
||||
x++; |
|
||||
ref byte residual = ref Unsafe.Add(ref resultBaseRef, x); |
|
||||
residual = (byte)(scan - PaethPredictor(left, above, upperLeft)); |
|
||||
sum += Numerics.Abs(unchecked((sbyte)residual)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Selects the scalar Paeth predictor.
|
|
||||
/// </summary>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static byte PaethPredictor(byte left, byte above, byte upperLeft) |
|
||||
{ |
|
||||
int p = left + above - upperLeft; |
|
||||
int distanceLeft = Numerics.Abs(p - left); |
|
||||
int distanceAbove = Numerics.Abs(p - above); |
|
||||
int distanceUpperLeft = Numerics.Abs(p - upperLeft); |
|
||||
|
|
||||
if (distanceLeft <= distanceAbove && distanceLeft <= distanceUpperLeft) |
|
||||
{ |
|
||||
return left; |
|
||||
} |
|
||||
|
|
||||
return distanceAbove <= distanceUpperLeft ? above : upperLeft; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Selects the AVX2 Paeth predictor.
|
|
||||
/// </summary>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static Vector256<byte> PaethPredictor( |
|
||||
Vector256<byte> left, |
|
||||
Vector256<byte> above, |
|
||||
Vector256<byte> upperLeft) |
|
||||
{ |
|
||||
Vector256<byte> zero = Vector256<byte>.Zero; |
|
||||
Vector256<byte> aboveMinusUpper = Avx2.SubtractSaturate(above, upperLeft); |
|
||||
Vector256<byte> leftMinusUpper = Avx2.SubtractSaturate(left, upperLeft); |
|
||||
Vector256<byte> distanceLeft = |
|
||||
Avx2.Or(Avx2.SubtractSaturate(upperLeft, above), aboveMinusUpper); |
|
||||
|
|
||||
Vector256<byte> distanceAbove = |
|
||||
Avx2.Or(Avx2.SubtractSaturate(upperLeft, left), leftMinusUpper); |
|
||||
|
|
||||
Vector256<byte> sameDirection = Avx2.CompareEqual( |
|
||||
Avx2.CompareEqual(aboveMinusUpper, zero), |
|
||||
Avx2.CompareEqual(leftMinusUpper, zero)); |
|
||||
|
|
||||
Vector256<byte> distanceUpper = Avx2.Or( |
|
||||
sameDirection, |
|
||||
Avx2.Or( |
|
||||
Avx2.SubtractSaturate(distanceAbove, distanceLeft), |
|
||||
Avx2.SubtractSaturate(distanceLeft, distanceAbove))); |
|
||||
|
|
||||
Vector256<byte> minimumAboveUpper = Avx2.Min(distanceUpper, distanceAbove); |
|
||||
Vector256<byte> aboveOrUpper = Avx2.BlendVariable( |
|
||||
upperLeft, |
|
||||
above, |
|
||||
Avx2.CompareEqual(minimumAboveUpper, distanceAbove)); |
|
||||
|
|
||||
return Avx2.BlendVariable( |
|
||||
aboveOrUpper, |
|
||||
left, |
|
||||
Avx2.CompareEqual(Avx2.Min(minimumAboveUpper, distanceLeft), distanceLeft)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Selects the portable vector Paeth predictor.
|
|
||||
/// </summary>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static Vector<byte> PaethPredictor( |
|
||||
Vector<byte> left, |
|
||||
Vector<byte> above, |
|
||||
Vector<byte> upperLeft) |
|
||||
{ |
|
||||
Vector.Widen(left, out Vector<ushort> leftLow, out Vector<ushort> leftHigh); |
|
||||
Vector.Widen(above, out Vector<ushort> aboveLow, out Vector<ushort> aboveHigh); |
|
||||
Vector.Widen(upperLeft, out Vector<ushort> upperLow, out Vector<ushort> upperHigh); |
|
||||
|
|
||||
Vector<short> lower = PaethPredictor( |
|
||||
Vector.AsVectorInt16(leftLow), |
|
||||
Vector.AsVectorInt16(aboveLow), |
|
||||
Vector.AsVectorInt16(upperLow)); |
|
||||
|
|
||||
Vector<short> upper = PaethPredictor( |
|
||||
Vector.AsVectorInt16(leftHigh), |
|
||||
Vector.AsVectorInt16(aboveHigh), |
|
||||
Vector.AsVectorInt16(upperHigh)); |
|
||||
|
|
||||
return Vector.AsVectorByte(Vector.Narrow(lower, upper)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Selects the portable widened Paeth predictor.
|
|
||||
/// </summary>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static Vector<short> PaethPredictor( |
|
||||
Vector<short> left, |
|
||||
Vector<short> above, |
|
||||
Vector<short> upperLeft) |
|
||||
{ |
|
||||
Vector<short> p = left + above - upperLeft; |
|
||||
Vector<short> distanceLeft = Vector.Abs(p - left); |
|
||||
Vector<short> distanceAbove = Vector.Abs(p - above); |
|
||||
Vector<short> distanceUpper = Vector.Abs(p - upperLeft); |
|
||||
|
|
||||
Vector<short> chooseLeft = Vector.BitwiseAnd( |
|
||||
Vector.LessThanOrEqual(distanceLeft, distanceAbove), |
|
||||
Vector.LessThanOrEqual(distanceLeft, distanceUpper)); |
|
||||
|
|
||||
return Vector.ConditionalSelect( |
|
||||
chooseLeft, |
|
||||
left, |
|
||||
Vector.ConditionalSelect( |
|
||||
Vector.LessThanOrEqual(distanceAbove, distanceUpper), |
|
||||
above, |
|
||||
upperLeft)); |
|
||||
} |
|
||||
} |
|
||||
@ -1,65 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using BenchmarkDotNet.Attributes; |
|
||||
using SixLabors.ImageSharp.Common.Helpers; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath; |
|
||||
|
|
||||
public class AddSpan |
|
||||
{ |
|
||||
private byte[] scalarValues = null!; |
|
||||
private byte[] tensorValues = null!; |
|
||||
private byte[] addends = null!; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the number of values to add.
|
|
||||
/// </summary>
|
|
||||
[Params(32, 257, 2048)] |
|
||||
public int Length { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates equivalent deterministic inputs for both implementations.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.scalarValues = new byte[this.Length]; |
|
||||
this.tensorValues = new byte[this.Length]; |
|
||||
this.addends = new byte[this.Length]; |
|
||||
|
|
||||
for (int i = 0; i < this.Length; i++) |
|
||||
{ |
|
||||
byte value = (byte)((i * 17) + 31); |
|
||||
this.scalarValues[i] = value; |
|
||||
this.tensorValues[i] = value; |
|
||||
this.addends[i] = (byte)((i * 29) + 7); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds the values with a scalar loop.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the mutated data observable to the benchmark harness.</returns>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
public byte Scalar() |
|
||||
{ |
|
||||
for (int i = 0; i < this.scalarValues.Length; i++) |
|
||||
{ |
|
||||
this.scalarValues[i] += this.addends[i]; |
|
||||
} |
|
||||
|
|
||||
return this.scalarValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds the values with the tensor compatibility pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the mutated data observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public byte TensorPipeline() |
|
||||
{ |
|
||||
TensorPrimitives_.Add<byte>(this.tensorValues, this.addends, this.tensorValues); |
|
||||
return this.tensorValues[0]; |
|
||||
} |
|
||||
} |
|
||||
@ -1,61 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using BenchmarkDotNet.Attributes; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath; |
|
||||
|
|
||||
public class NormalizeSpan |
|
||||
{ |
|
||||
private float[] scalarValues = null!; |
|
||||
private float[] tensorValues = null!; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the number of values to normalize.
|
|
||||
/// </summary>
|
|
||||
[Params(7, 32, 257, 2048)] |
|
||||
public int Length { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates equivalent deterministic inputs for both implementations.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.scalarValues = new float[this.Length]; |
|
||||
this.tensorValues = new float[this.Length]; |
|
||||
|
|
||||
for (int i = 0; i < this.scalarValues.Length; i++) |
|
||||
{ |
|
||||
float value = ((i * 17) % 251) + 1; |
|
||||
this.scalarValues[i] = value; |
|
||||
this.tensorValues[i] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Normalizes the values with a scalar loop.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the mutated data observable to the benchmark harness.</returns>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
public float Scalar() |
|
||||
{ |
|
||||
for (int i = 0; i < this.scalarValues.Length; i++) |
|
||||
{ |
|
||||
this.scalarValues[i] /= 4096F; |
|
||||
} |
|
||||
|
|
||||
return this.scalarValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Normalizes the values with the tensor compatibility pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the mutated data observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public float TensorPipeline() |
|
||||
{ |
|
||||
Numerics.Normalize(this.tensorValues, 4096F); |
|
||||
return this.tensorValues[0]; |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,175 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
using SixLabors.ImageSharp.Common.Helpers; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Exposes every floating-point tensor compatibility operation for assembly inspection.
|
||||
|
/// </summary>
|
||||
|
[Config(typeof(Config.Analysis))] |
||||
|
public class TensorPrimitivesAssembly |
||||
|
{ |
||||
|
private const int Count = 2048; |
||||
|
|
||||
|
private readonly float[] x = new float[Count]; |
||||
|
private readonly float[] y = new float[Count]; |
||||
|
private readonly float[] destination = new float[Count]; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Populates the input spans with deterministic non-uniform values.
|
||||
|
/// </summary>
|
||||
|
[GlobalSetup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
for (int i = 0; i < Count; i++) |
||||
|
{ |
||||
|
this.x[i] = ((i * 17) % 251) + 1; |
||||
|
this.y[i] = ((i * 29) % 251) + 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adds two floating-point spans.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The first result, which keeps the destination observable.</returns>
|
||||
|
[Benchmark] |
||||
|
public float Add() |
||||
|
{ |
||||
|
TensorPrimitives_.Add<float>(this.x, this.y, this.destination); |
||||
|
return this.destination[0]; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clamps a floating-point span between scalar bounds.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The first result, which keeps the destination observable.</returns>
|
||||
|
[Benchmark] |
||||
|
public float Clamp() |
||||
|
{ |
||||
|
TensorPrimitives_.Clamp(this.x, 64F, 128F, this.destination); |
||||
|
return this.destination[0]; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Divides a floating-point span by a scalar.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The first result, which keeps the destination observable.</returns>
|
||||
|
[Benchmark] |
||||
|
public float Divide() |
||||
|
{ |
||||
|
TensorPrimitives_.Divide(this.x, 4096F, this.destination); |
||||
|
return this.destination[0]; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Computes the element-wise maximum of a floating-point span and a scalar.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The first result, which keeps the destination observable.</returns>
|
||||
|
[Benchmark] |
||||
|
public float Max() |
||||
|
{ |
||||
|
TensorPrimitives_.Max(this.x, 64F, this.destination); |
||||
|
return this.destination[0]; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Multiplies a floating-point span by a scalar.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The first result, which keeps the destination observable.</returns>
|
||||
|
[Benchmark] |
||||
|
public float Multiply() |
||||
|
{ |
||||
|
TensorPrimitives_.Multiply(this.x, 0.5F, this.destination); |
||||
|
return this.destination[0]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Exposes integral addition specializations for assembly inspection.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The integral element type.</typeparam>
|
||||
|
[Config(typeof(Config.Analysis))] |
||||
|
[GenericTypeArguments(typeof(byte))] |
||||
|
[GenericTypeArguments(typeof(uint))] |
||||
|
public class TensorPrimitivesIntegralAddAssembly<T> |
||||
|
where T : unmanaged, INumber<T> |
||||
|
{ |
||||
|
private const int Count = 2048; |
||||
|
|
||||
|
private readonly T[] x = new T[Count]; |
||||
|
private readonly T[] y = new T[Count]; |
||||
|
private readonly T[] destination = new T[Count]; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Populates the input spans with deterministic non-uniform values.
|
||||
|
/// </summary>
|
||||
|
[GlobalSetup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
for (int i = 0; i < Count; i++) |
||||
|
{ |
||||
|
this.x[i] = T.CreateTruncating((i * 17) + 31); |
||||
|
this.y[i] = T.CreateTruncating((i * 29) + 7); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adds two integral spans.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The first result, which keeps the destination observable.</returns>
|
||||
|
[Benchmark] |
||||
|
public T Add() |
||||
|
{ |
||||
|
TensorPrimitives_.Add<T>(this.x, this.y, this.destination); |
||||
|
return this.destination[0]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Exposes integral clamp specializations for assembly inspection.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The integral element type.</typeparam>
|
||||
|
[Config(typeof(Config.Analysis))] |
||||
|
[GenericTypeArguments(typeof(byte))] |
||||
|
[GenericTypeArguments(typeof(uint))] |
||||
|
[GenericTypeArguments(typeof(int))] |
||||
|
public class TensorPrimitivesIntegralClampAssembly<T> |
||||
|
where T : unmanaged, INumber<T> |
||||
|
{ |
||||
|
private const int Count = 2048; |
||||
|
|
||||
|
private readonly T[] source = new T[Count]; |
||||
|
private readonly T[] destination = new T[Count]; |
||||
|
private T min; |
||||
|
private T max; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Populates the input span and scalar bounds with deterministic values.
|
||||
|
/// </summary>
|
||||
|
[GlobalSetup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.min = T.CreateTruncating(64); |
||||
|
this.max = T.CreateTruncating(128); |
||||
|
|
||||
|
for (int i = 0; i < Count; i++) |
||||
|
{ |
||||
|
this.source[i] = T.CreateTruncating((i * 31) % 257); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clamps an integral span between scalar bounds.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The first result, which keeps the destination observable.</returns>
|
||||
|
[Benchmark] |
||||
|
public T Clamp() |
||||
|
{ |
||||
|
TensorPrimitives_.Clamp(this.source, this.min, this.max, this.destination); |
||||
|
return this.destination[0]; |
||||
|
} |
||||
|
} |
||||
@ -1,813 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using System.Runtime.Intrinsics; |
|
||||
using System.Runtime.Intrinsics.X86; |
|
||||
using BenchmarkDotNet.Attributes; |
|
||||
using SixLabors.ImageSharp.Common.Helpers; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath; |
|
||||
|
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
|
||||
public class TensorPrimitivesJpegMultiplyAssemblyComparison |
|
||||
#pragma warning restore SA1649 // File name should match first type name
|
|
||||
{ |
|
||||
private readonly float multiplier = -1F; |
|
||||
private float[] legacyValues = null!; |
|
||||
private float[] tensorValues = null!; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates equivalent stable inputs for both implementations.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.legacyValues = new float[256]; |
|
||||
this.tensorValues = new float[256]; |
|
||||
|
|
||||
for (int i = 0; i < this.legacyValues.Length; i++) |
|
||||
{ |
|
||||
float value = ((i * 17) % 251) + 1; |
|
||||
this.legacyValues[i] = value; |
|
||||
this.tensorValues[i] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Multiplies the row with the retired JPEG AVX pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
public float Legacy() |
|
||||
{ |
|
||||
LegacyMultiply(this.legacyValues, this.multiplier); |
|
||||
return this.legacyValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Multiplies the row with the tensor compatibility pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public float Tensor() |
|
||||
{ |
|
||||
TensorPrimitives_.Multiply(this.tensorValues, this.multiplier, this.tensorValues); |
|
||||
return this.tensorValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reproduces the retired JPEG multiplication loop for assembly comparison.
|
|
||||
/// </summary>
|
|
||||
/// <param name="target">The row to multiply.</param>
|
|
||||
/// <param name="multiplier">The scalar multiplier.</param>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static void LegacyMultiply(Span<float> target, float multiplier) |
|
||||
{ |
|
||||
ref Vector256<float> targetVector = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(target)); |
|
||||
nuint count = (uint)target.Length / (uint)Vector256<float>.Count; |
|
||||
Vector256<float> multiplierVector = Vector256.Create(multiplier); |
|
||||
|
|
||||
for (nuint i = 0; i < count; i++) |
|
||||
{ |
|
||||
Unsafe.Add(ref targetVector, i) = Avx.Multiply(Unsafe.Add(ref targetVector, i), multiplierVector); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class TensorPrimitivesNormalizeAssemblyComparison |
|
||||
{ |
|
||||
private readonly float divisor = -1F; |
|
||||
private float[] legacyValues = null!; |
|
||||
private float[] tensorValues = null!; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates equivalent stable inputs for both implementations.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.legacyValues = new float[7]; |
|
||||
this.tensorValues = new float[7]; |
|
||||
|
|
||||
for (int i = 0; i < this.legacyValues.Length; i++) |
|
||||
{ |
|
||||
float value = ((i * 17) % 251) + 1; |
|
||||
this.legacyValues[i] = value; |
|
||||
this.tensorValues[i] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Normalizes the values with the retired fixed-width pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
public float Legacy() |
|
||||
{ |
|
||||
LegacyNormalize(this.legacyValues, this.divisor); |
|
||||
return this.legacyValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Normalizes the values with the tensor compatibility pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public float Tensor() |
|
||||
{ |
|
||||
Numerics.Normalize(this.tensorValues, this.divisor); |
|
||||
return this.tensorValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reproduces the retired normalization loop for assembly comparison.
|
|
||||
/// </summary>
|
|
||||
/// <param name="span">The values to normalize.</param>
|
|
||||
/// <param name="sum">The scalar divisor.</param>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static void LegacyNormalize(Span<float> span, float sum) |
|
||||
{ |
|
||||
ref float start = ref MemoryMarshal.GetReference(span); |
|
||||
ref float vectorEnd = ref Unsafe.Add(ref start, span.Length & ~7); |
|
||||
Vector256<float> sum256 = Vector256.Create(sum); |
|
||||
|
|
||||
while (Unsafe.IsAddressLessThan(ref start, ref vectorEnd)) |
|
||||
{ |
|
||||
Unsafe.As<float, Vector256<float>>(ref start) /= sum256; |
|
||||
start = ref Unsafe.Add(ref start, (nuint)8); |
|
||||
} |
|
||||
|
|
||||
if ((span.Length & 7) >= 4) |
|
||||
{ |
|
||||
Unsafe.As<float, Vector128<float>>(ref start) /= sum256.GetLower(); |
|
||||
start = ref Unsafe.Add(ref start, (nuint)4); |
|
||||
} |
|
||||
|
|
||||
ref float end = ref Unsafe.Add(ref start, span.Length & 3); |
|
||||
|
|
||||
while (Unsafe.IsAddressLessThan(ref start, ref end)) |
|
||||
{ |
|
||||
start /= sum; |
|
||||
start = ref Unsafe.Add(ref start, (nuint)1); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class TensorPrimitivesUInt32AssemblyComparison |
|
||||
{ |
|
||||
private uint[] x = null!; |
|
||||
private uint[] y = null!; |
|
||||
private uint[] legacyDestination = null!; |
|
||||
private uint[] tensorDestination = null!; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates deterministic histogram inputs and independent destinations.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.x = new uint[2048]; |
|
||||
this.y = new uint[2048]; |
|
||||
this.legacyDestination = new uint[2048]; |
|
||||
this.tensorDestination = new uint[2048]; |
|
||||
|
|
||||
for (int i = 0; i < this.x.Length; i++) |
|
||||
{ |
|
||||
this.x[i] = (uint)((i * 17) + 31); |
|
||||
this.y[i] = (uint)((i * 29) + 7); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds histogram bins with the retired four-vector AVX2 pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
public uint Legacy() |
|
||||
{ |
|
||||
LegacyAdd(this.x, this.y, this.legacyDestination); |
|
||||
return this.legacyDestination[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds histogram bins with the tensor compatibility pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public uint Tensor() |
|
||||
{ |
|
||||
TensorPrimitives_.Add<uint>(this.x, this.y, this.tensorDestination); |
|
||||
return this.tensorDestination[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reproduces the retired WebP histogram addition loop for assembly comparison.
|
|
||||
/// </summary>
|
|
||||
/// <param name="x">The first histogram.</param>
|
|
||||
/// <param name="y">The second histogram.</param>
|
|
||||
/// <param name="destination">The destination receiving the sums.</param>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static void LegacyAdd(ReadOnlySpan<uint> x, ReadOnlySpan<uint> y, Span<uint> destination) |
|
||||
{ |
|
||||
ref uint xRef = ref MemoryMarshal.GetReference(x); |
|
||||
ref uint yRef = ref MemoryMarshal.GetReference(y); |
|
||||
ref uint destinationRef = ref MemoryMarshal.GetReference(destination); |
|
||||
|
|
||||
nuint index = 0; |
|
||||
|
|
||||
do |
|
||||
{ |
|
||||
Vector256<uint> x0 = Unsafe.As<uint, Vector256<uint>>(ref Unsafe.Add(ref xRef, index)); |
|
||||
Vector256<uint> x1 = Unsafe.As<uint, Vector256<uint>>(ref Unsafe.Add(ref xRef, index + 8)); |
|
||||
Vector256<uint> x2 = Unsafe.As<uint, Vector256<uint>>(ref Unsafe.Add(ref xRef, index + 16)); |
|
||||
Vector256<uint> x3 = Unsafe.As<uint, Vector256<uint>>(ref Unsafe.Add(ref xRef, index + 24)); |
|
||||
Vector256<uint> y0 = Unsafe.As<uint, Vector256<uint>>(ref Unsafe.Add(ref yRef, index)); |
|
||||
Vector256<uint> y1 = Unsafe.As<uint, Vector256<uint>>(ref Unsafe.Add(ref yRef, index + 8)); |
|
||||
Vector256<uint> y2 = Unsafe.As<uint, Vector256<uint>>(ref Unsafe.Add(ref yRef, index + 16)); |
|
||||
Vector256<uint> y3 = Unsafe.As<uint, Vector256<uint>>(ref Unsafe.Add(ref yRef, index + 24)); |
|
||||
|
|
||||
Unsafe.As<uint, Vector256<uint>>(ref Unsafe.Add(ref destinationRef, index)) = Avx2.Add(x0, y0); |
|
||||
Unsafe.As<uint, Vector256<uint>>(ref Unsafe.Add(ref destinationRef, index + 8)) = Avx2.Add(x1, y1); |
|
||||
Unsafe.As<uint, Vector256<uint>>(ref Unsafe.Add(ref destinationRef, index + 16)) = Avx2.Add(x2, y2); |
|
||||
Unsafe.As<uint, Vector256<uint>>(ref Unsafe.Add(ref destinationRef, index + 24)) = Avx2.Add(x3, y3); |
|
||||
index += 32; |
|
||||
} |
|
||||
while (index <= (uint)x.Length - 32); |
|
||||
|
|
||||
for (int i = (int)index; i < x.Length; i++) |
|
||||
{ |
|
||||
destination[i] = x[i] + y[i]; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class TensorPrimitivesByteAssemblyComparison |
|
||||
{ |
|
||||
private byte[] x = null!; |
|
||||
private byte[] y = null!; |
|
||||
private byte[] legacyDestination = null!; |
|
||||
private byte[] tensorDestination = null!; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates deterministic byte inputs and independent destinations.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.x = new byte[2048]; |
|
||||
this.y = new byte[2048]; |
|
||||
this.legacyDestination = new byte[2048]; |
|
||||
this.tensorDestination = new byte[2048]; |
|
||||
|
|
||||
for (int i = 0; i < this.x.Length; i++) |
|
||||
{ |
|
||||
this.x[i] = (byte)((i * 17) + 31); |
|
||||
this.y[i] = (byte)((i * 29) + 7); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds bytes with the retired WebP AVX2 pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
public byte Legacy() |
|
||||
{ |
|
||||
LegacyAdd(this.x, this.y, this.legacyDestination); |
|
||||
return this.legacyDestination[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds bytes with the tensor compatibility pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public byte Tensor() |
|
||||
{ |
|
||||
TensorPrimitives_.Add<byte>(this.x, this.y, this.tensorDestination); |
|
||||
return this.tensorDestination[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reproduces the retired WebP byte addition loop for assembly comparison.
|
|
||||
/// </summary>
|
|
||||
/// <param name="x">The first input.</param>
|
|
||||
/// <param name="y">The second input.</param>
|
|
||||
/// <param name="destination">The destination receiving modulo-256 sums.</param>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static void LegacyAdd(ReadOnlySpan<byte> x, ReadOnlySpan<byte> y, Span<byte> destination) |
|
||||
{ |
|
||||
ref byte xRef = ref MemoryMarshal.GetReference(x); |
|
||||
ref byte yRef = ref MemoryMarshal.GetReference(y); |
|
||||
ref byte destinationRef = ref MemoryMarshal.GetReference(destination); |
|
||||
|
|
||||
nuint i; |
|
||||
int maxPosition = x.Length & ~31; |
|
||||
|
|
||||
for (i = 0; i < (uint)maxPosition; i += 32) |
|
||||
{ |
|
||||
Vector256<int> x0 = Unsafe.As<byte, Vector256<int>>(ref Unsafe.Add(ref xRef, i)); |
|
||||
Vector256<int> y0 = Unsafe.As<byte, Vector256<int>>(ref Unsafe.Add(ref yRef, i)); |
|
||||
Vector256<byte> result = x0.AsByte() + y0.AsByte(); |
|
||||
Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref destinationRef, i)) = result; |
|
||||
} |
|
||||
|
|
||||
for (; i < (uint)x.Length; i++) |
|
||||
{ |
|
||||
Unsafe.Add(ref destinationRef, i) = (byte)(Unsafe.Add(ref xRef, i) + Unsafe.Add(ref yRef, i)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class TensorPrimitivesSingleAddAssemblyComparison |
|
||||
{ |
|
||||
private float[] legacyTarget = null!; |
|
||||
private float[] tensorTarget = null!; |
|
||||
private float[] source = null!; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates deterministic JPEG row inputs.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.legacyTarget = new float[2048]; |
|
||||
this.tensorTarget = new float[2048]; |
|
||||
this.source = new float[2048]; |
|
||||
|
|
||||
for (int i = 0; i < this.source.Length; i++) |
|
||||
{ |
|
||||
float value = ((i * 17) % 251) + 1; |
|
||||
this.legacyTarget[i] = value; |
|
||||
this.tensorTarget[i] = value; |
|
||||
this.source[i] = ((i * 29) % 31) - 15; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds JPEG row values with the retired AVX pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
public float Legacy() |
|
||||
{ |
|
||||
LegacyAdd(this.legacyTarget, this.source); |
|
||||
return this.legacyTarget[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds JPEG row values with the tensor compatibility pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public float Tensor() |
|
||||
{ |
|
||||
TensorPrimitives_.Add<float>(this.tensorTarget, this.source, this.tensorTarget); |
|
||||
return this.tensorTarget[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reproduces the retired JPEG row addition loop for assembly comparison.
|
|
||||
/// </summary>
|
|
||||
/// <param name="target">The destination row.</param>
|
|
||||
/// <param name="source">The row added to the destination.</param>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static void LegacyAdd(Span<float> target, ReadOnlySpan<float> source) |
|
||||
{ |
|
||||
ref Vector256<float> targetVector = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(target)); |
|
||||
ref Vector256<float> sourceVector = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(source)); |
|
||||
nuint count = (uint)source.Length / (uint)Vector256<float>.Count; |
|
||||
|
|
||||
for (nuint i = 0; i < count; i++) |
|
||||
{ |
|
||||
Unsafe.Add(ref targetVector, i) = Avx.Add(Unsafe.Add(ref targetVector, i), Unsafe.Add(ref sourceVector, i)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[GenericTypeArguments(typeof(byte))] |
|
||||
[GenericTypeArguments(typeof(uint))] |
|
||||
[GenericTypeArguments(typeof(int))] |
|
||||
[GenericTypeArguments(typeof(float))] |
|
||||
[GenericTypeArguments(typeof(double))] |
|
||||
public class TensorPrimitivesClampAssemblyComparison<T> |
|
||||
where T : unmanaged, INumber<T> |
|
||||
{ |
|
||||
private T[] legacyValues = null!; |
|
||||
private T[] tensorValues = null!; |
|
||||
private T min; |
|
||||
private T max; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates deterministic clamp inputs for the current element type.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.legacyValues = new T[2048]; |
|
||||
this.tensorValues = new T[2048]; |
|
||||
this.min = T.CreateTruncating(64); |
|
||||
this.max = T.CreateTruncating(128); |
|
||||
|
|
||||
for (int i = 0; i < this.legacyValues.Length; i++) |
|
||||
{ |
|
||||
T value = T.CreateTruncating((i * 31) % 257); |
|
||||
this.legacyValues[i] = value; |
|
||||
this.tensorValues[i] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Clamps values with the retired <see cref="Vector{T}"/> pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
public T Legacy() |
|
||||
{ |
|
||||
LegacyClamp(this.legacyValues, this.min, this.max); |
|
||||
return this.legacyValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Clamps values with the tensor compatibility pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public T Tensor() |
|
||||
{ |
|
||||
TensorPrimitives_.Clamp(this.tensorValues, this.min, this.max, this.tensorValues); |
|
||||
return this.tensorValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reproduces the retired clamp pipeline for assembly comparison.
|
|
||||
/// </summary>
|
|
||||
/// <param name="span">The values to clamp.</param>
|
|
||||
/// <param name="min">The inclusive lower bound.</param>
|
|
||||
/// <param name="max">The inclusive upper bound.</param>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static void LegacyClamp(Span<T> span, T min, T max) |
|
||||
{ |
|
||||
int remainder = Numerics.ModuloP2(span.Length, Vector<T>.Count); |
|
||||
int adjustedCount = span.Length - remainder; |
|
||||
|
|
||||
if (adjustedCount > 0) |
|
||||
{ |
|
||||
Vector<T> vectorMin = new(min); |
|
||||
Vector<T> vectorMax = new(max); |
|
||||
nint vectorCount = (nint)(uint)adjustedCount / Vector<T>.Count; |
|
||||
nint remainingVectors = Numerics.Modulo4(vectorCount); |
|
||||
nint unrolledVectors = vectorCount - remainingVectors; |
|
||||
|
|
||||
ref Vector<T> current0 = ref Unsafe.As<T, Vector<T>>(ref MemoryMarshal.GetReference(span)); |
|
||||
ref Vector<T> current1 = ref Unsafe.Add(ref current0, 1); |
|
||||
ref Vector<T> current2 = ref Unsafe.Add(ref current0, 2); |
|
||||
ref Vector<T> current3 = ref Unsafe.Add(ref current0, 3); |
|
||||
ref Vector<T> end = ref Unsafe.Add(ref current0, unrolledVectors); |
|
||||
|
|
||||
while (Unsafe.IsAddressLessThan(ref current0, ref end)) |
|
||||
{ |
|
||||
current0 = Vector.Min(Vector.Max(vectorMin, current0), vectorMax); |
|
||||
current1 = Vector.Min(Vector.Max(vectorMin, current1), vectorMax); |
|
||||
current2 = Vector.Min(Vector.Max(vectorMin, current2), vectorMax); |
|
||||
current3 = Vector.Min(Vector.Max(vectorMin, current3), vectorMax); |
|
||||
|
|
||||
current0 = ref Unsafe.Add(ref current0, 4); |
|
||||
current1 = ref Unsafe.Add(ref current1, 4); |
|
||||
current2 = ref Unsafe.Add(ref current2, 4); |
|
||||
current3 = ref Unsafe.Add(ref current3, 4); |
|
||||
} |
|
||||
|
|
||||
if (remainingVectors > 0) |
|
||||
{ |
|
||||
current0 = ref end; |
|
||||
end = ref Unsafe.Add(ref end, remainingVectors); |
|
||||
|
|
||||
while (Unsafe.IsAddressLessThan(ref current0, ref end)) |
|
||||
{ |
|
||||
current0 = Vector.Min(Vector.Max(vectorMin, current0), vectorMax); |
|
||||
current0 = ref Unsafe.Add(ref current0, 1); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
for (int i = adjustedCount; i < span.Length; i++) |
|
||||
{ |
|
||||
T value = span[i]; |
|
||||
span[i] = value > max ? max : value < min ? min : value; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class TensorPrimitivesIccMaxAssemblyComparison |
|
||||
{ |
|
||||
private Vector4[] legacyValues = null!; |
|
||||
private Vector4[] tensorValues = null!; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates deterministic ICC values containing positive and negative channels.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.legacyValues = new Vector4[512]; |
|
||||
this.tensorValues = new Vector4[512]; |
|
||||
|
|
||||
for (int i = 0; i < this.legacyValues.Length; i++) |
|
||||
{ |
|
||||
float value = ((i * 17) % 251) - 125; |
|
||||
Vector4 vector = new(value, value + 1, value - 1, value + 2); |
|
||||
this.legacyValues[i] = vector; |
|
||||
this.tensorValues[i] = vector; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Clips negative channels with the retired ICC pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
public float Legacy() |
|
||||
{ |
|
||||
for (int i = 0; i < this.legacyValues.Length; i++) |
|
||||
{ |
|
||||
this.legacyValues[i] = Vector4.Max(this.legacyValues[i], Vector4.Zero); |
|
||||
} |
|
||||
|
|
||||
return this.legacyValues[0].X; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Clips negative channels with the tensor compatibility pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public float Tensor() |
|
||||
{ |
|
||||
Span<float> values = MemoryMarshal.Cast<Vector4, float>(this.tensorValues.AsSpan()); |
|
||||
TensorPrimitives_.Max(values, 0F, values); |
|
||||
return values[0]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class TensorPrimitivesIccMultiplyAssemblyComparison |
|
||||
{ |
|
||||
private readonly float multiplier = 65280F / 65535F; |
|
||||
private Vector4[] source = null!; |
|
||||
private Vector4[] legacyDestination = null!; |
|
||||
private Vector4[] tensorDestination = null!; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates deterministic ICC inputs and independent destinations.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.source = new Vector4[512]; |
|
||||
this.legacyDestination = new Vector4[512]; |
|
||||
this.tensorDestination = new Vector4[512]; |
|
||||
|
|
||||
for (int i = 0; i < this.source.Length; i++) |
|
||||
{ |
|
||||
float value = ((i * 17) % 251) + 1; |
|
||||
this.source[i] = new Vector4(value, value + 1, value + 2, value + 3); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Multiplies ICC channels with the retired <see cref="Vector{T}"/> pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
public float Legacy() |
|
||||
{ |
|
||||
Span<float> source = MemoryMarshal.Cast<Vector4, float>(this.source.AsSpan()); |
|
||||
Span<float> destination = MemoryMarshal.Cast<Vector4, float>(this.legacyDestination.AsSpan()); |
|
||||
ref Vector<float> sourceVector = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source)); |
|
||||
ref Vector<float> destinationVector = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(destination)); |
|
||||
Vector<float> scale = new(this.multiplier); |
|
||||
nuint count = (uint)source.Length / (uint)Vector<float>.Count; |
|
||||
|
|
||||
for (nuint i = 0; i < count; i++) |
|
||||
{ |
|
||||
Unsafe.Add(ref destinationVector, i) = Unsafe.Add(ref sourceVector, i) * scale; |
|
||||
} |
|
||||
|
|
||||
return destination[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Multiplies ICC channels with the tensor compatibility pipeline.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public float Tensor() |
|
||||
{ |
|
||||
Span<float> source = MemoryMarshal.Cast<Vector4, float>(this.source.AsSpan()); |
|
||||
Span<float> destination = MemoryMarshal.Cast<Vector4, float>(this.tensorDestination.AsSpan()); |
|
||||
TensorPrimitives_.Multiply(source, this.multiplier, destination); |
|
||||
return destination[0]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#if NET10_0_OR_GREATER
|
|
||||
[GenericTypeArguments(typeof(byte))] |
|
||||
[GenericTypeArguments(typeof(uint))] |
|
||||
[GenericTypeArguments(typeof(float))] |
|
||||
public class TensorPrimitivesRuntimeAddAssemblyComparison<T> |
|
||||
where T : unmanaged, INumber<T> |
|
||||
{ |
|
||||
private T[] x = null!; |
|
||||
private T[] y = null!; |
|
||||
private T[] compatibilityDestination = null!; |
|
||||
private T[] runtimeDestination = null!; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates deterministic inputs and independent destinations.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.x = new T[2048]; |
|
||||
this.y = new T[2048]; |
|
||||
this.compatibilityDestination = new T[2048]; |
|
||||
this.runtimeDestination = new T[2048]; |
|
||||
|
|
||||
for (int i = 0; i < this.x.Length; i++) |
|
||||
{ |
|
||||
this.x[i] = T.CreateTruncating((i * 17) + 31); |
|
||||
this.y[i] = T.CreateTruncating((i * 29) + 7); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds values with the compatibility implementation.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
public T Compatibility() |
|
||||
{ |
|
||||
TensorPrimitives_.Add<T>(this.x, this.y, this.compatibilityDestination); |
|
||||
return this.compatibilityDestination[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds values with the .NET runtime implementation.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public T Runtime() |
|
||||
{ |
|
||||
System.Numerics.Tensors.TensorPrimitives.Add<T>(this.x, this.y, this.runtimeDestination); |
|
||||
return this.runtimeDestination[0]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[GenericTypeArguments(typeof(byte))] |
|
||||
[GenericTypeArguments(typeof(uint))] |
|
||||
[GenericTypeArguments(typeof(int))] |
|
||||
[GenericTypeArguments(typeof(float))] |
|
||||
[GenericTypeArguments(typeof(double))] |
|
||||
public class TensorPrimitivesRuntimeClampAssemblyComparison<T> |
|
||||
where T : unmanaged, INumber<T> |
|
||||
{ |
|
||||
private T[] compatibilityValues = null!; |
|
||||
private T[] runtimeValues = null!; |
|
||||
private T min; |
|
||||
private T max; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates deterministic inputs for both implementations.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.compatibilityValues = new T[2048]; |
|
||||
this.runtimeValues = new T[2048]; |
|
||||
this.min = T.CreateTruncating(64); |
|
||||
this.max = T.CreateTruncating(128); |
|
||||
|
|
||||
for (int i = 0; i < this.compatibilityValues.Length; i++) |
|
||||
{ |
|
||||
T value = T.CreateTruncating((i * 31) % 257); |
|
||||
this.compatibilityValues[i] = value; |
|
||||
this.runtimeValues[i] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Clamps values with the compatibility implementation.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
public T Compatibility() |
|
||||
{ |
|
||||
TensorPrimitives_.Clamp(this.compatibilityValues, this.min, this.max, this.compatibilityValues); |
|
||||
return this.compatibilityValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Clamps values with the .NET runtime implementation.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public T Runtime() |
|
||||
{ |
|
||||
System.Numerics.Tensors.TensorPrimitives.Clamp(this.runtimeValues, this.min, this.max, this.runtimeValues); |
|
||||
return this.runtimeValues[0]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class TensorPrimitivesRuntimeSingleScalarAssemblyComparison |
|
||||
{ |
|
||||
private readonly float scalar = -1F; |
|
||||
private float[] compatibilityValues = null!; |
|
||||
private float[] runtimeValues = null!; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates equivalent stable inputs for both implementations.
|
|
||||
/// </summary>
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.compatibilityValues = new float[2048]; |
|
||||
this.runtimeValues = new float[2048]; |
|
||||
|
|
||||
for (int i = 0; i < this.compatibilityValues.Length; i++) |
|
||||
{ |
|
||||
float value = ((i * 17) % 251) + 1; |
|
||||
this.compatibilityValues[i] = value; |
|
||||
this.runtimeValues[i] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Divides values with the compatibility implementation.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public float CompatibilityDivide() |
|
||||
{ |
|
||||
TensorPrimitives_.Divide(this.compatibilityValues, this.scalar, this.compatibilityValues); |
|
||||
return this.compatibilityValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Divides values with the .NET runtime implementation.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public float RuntimeDivide() |
|
||||
{ |
|
||||
System.Numerics.Tensors.TensorPrimitives.Divide(this.runtimeValues, this.scalar, this.runtimeValues); |
|
||||
return this.runtimeValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Computes maximum values with the compatibility implementation.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public float CompatibilityMax() |
|
||||
{ |
|
||||
TensorPrimitives_.Max(this.compatibilityValues, 0F, this.compatibilityValues); |
|
||||
return this.compatibilityValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Computes maximum values with the .NET runtime implementation.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public float RuntimeMax() |
|
||||
{ |
|
||||
System.Numerics.Tensors.TensorPrimitives.Max(this.runtimeValues, 0F, this.runtimeValues); |
|
||||
return this.runtimeValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Multiplies values with the compatibility implementation.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public float CompatibilityMultiply() |
|
||||
{ |
|
||||
TensorPrimitives_.Multiply(this.compatibilityValues, this.scalar, this.compatibilityValues); |
|
||||
return this.compatibilityValues[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Multiplies values with the .NET runtime implementation.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The first result, which keeps the writes observable to the benchmark harness.</returns>
|
|
||||
[Benchmark] |
|
||||
public float RuntimeMultiply() |
|
||||
{ |
|
||||
System.Numerics.Tensors.TensorPrimitives.Multiply(this.runtimeValues, this.scalar, this.runtimeValues); |
|
||||
return this.runtimeValues[0]; |
|
||||
} |
|
||||
} |
|
||||
#endif
|
|
||||
File diff suppressed because it is too large
Loading…
Reference in new issue