|
|
|
@ -46,53 +46,6 @@ namespace SixLabors.ImageSharp |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Convert 'source.Length' <see cref="float"/> values normalized into [0..1] from 'source'
|
|
|
|
/// into 'dest' buffer of <see cref="byte"/>. The values are scaled up into [0-255] and rounded.
|
|
|
|
/// The implementation is SIMD optimized and works only with `source.Length` divisible by 8/>.
|
|
|
|
/// Based on:
|
|
|
|
/// <see>
|
|
|
|
/// <cref>http://lolengine.net/blog/2011/3/20/understanding-fast-float-integer-conversions</cref>
|
|
|
|
/// </see>
|
|
|
|
/// </summary>
|
|
|
|
internal static void BulkConvertNormalizedFloatToByte(ReadOnlySpan<float> source, Span<byte> dest) |
|
|
|
{ |
|
|
|
GuardAvx2(nameof(BulkConvertNormalizedFloatToByte)); |
|
|
|
|
|
|
|
DebugGuard.IsTrue((source.Length % Vector<float>.Count) == 0, nameof(source), "source.Length should be divisable by Vector<float>.Count!"); |
|
|
|
|
|
|
|
if (source.Length == 0) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
ref Vector<float> srcBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source)); |
|
|
|
ref Octet.OfByte destBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(dest)); |
|
|
|
int n = source.Length / 8; |
|
|
|
|
|
|
|
Vector<float> magick = new Vector<float>(32768.0f); |
|
|
|
Vector<float> scale = new Vector<float>(255f) / new Vector<float>(256f); |
|
|
|
|
|
|
|
// need to copy to a temporary struct, because
|
|
|
|
// SimdUtils.Octet.OfUInt32 temp = Unsafe.As<Vector<float>, SimdUtils.Octet.OfUInt32>(ref x)
|
|
|
|
// does not work. TODO: This might be a CoreClr bug, need to ask/report
|
|
|
|
var temp = default(Octet.OfUInt32); |
|
|
|
ref Vector<float> tempRef = ref Unsafe.As<Octet.OfUInt32, Vector<float>>(ref temp); |
|
|
|
|
|
|
|
for (int i = 0; i < n; i++) |
|
|
|
{ |
|
|
|
// union { float f; uint32_t i; } u;
|
|
|
|
// u.f = 32768.0f + x * (255.0f / 256.0f);
|
|
|
|
// return (uint8_t)u.i;
|
|
|
|
Vector<float> x = Unsafe.Add(ref srcBase, i); |
|
|
|
x = (x * scale) + magick; |
|
|
|
tempRef = x; |
|
|
|
|
|
|
|
ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i); |
|
|
|
d.LoadFrom(ref temp); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// SIMD optimized implementation for <see cref="SimdUtils.BulkConvertByteToNormalizedFloat"/>.
|
|
|
|
/// Works only with `dest.Length` divisible by 8.
|
|
|
|
@ -165,7 +118,7 @@ namespace SixLabors.ImageSharp |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Same as <see cref="BulkConvertNormalizedFloatToByte"/> but clamps overflown values before conversion.
|
|
|
|
/// Implementation of <see cref="SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows"/> which is faster on older runtimes.
|
|
|
|
/// </summary>
|
|
|
|
internal static void BulkConvertNormalizedFloatToByteClampOverflows(ReadOnlySpan<float> source, Span<byte> dest) |
|
|
|
{ |
|
|
|
@ -207,6 +160,53 @@ namespace SixLabors.ImageSharp |
|
|
|
d.LoadFrom(ref temp); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Convert 'source.Length' <see cref="float"/> values normalized into [0..1] from 'source'
|
|
|
|
/// into 'dest' buffer of <see cref="byte"/>. The values are scaled up into [0-255] and rounded.
|
|
|
|
/// The implementation is SIMD optimized and works only with `source.Length` divisible by 8.
|
|
|
|
/// Based on:
|
|
|
|
/// <see>
|
|
|
|
/// <cref>http://lolengine.net/blog/2011/3/20/understanding-fast-float-integer-conversions</cref>
|
|
|
|
/// </see>
|
|
|
|
/// </summary>
|
|
|
|
internal static void BulkConvertNormalizedFloatToByte(ReadOnlySpan<float> source, Span<byte> dest) |
|
|
|
{ |
|
|
|
GuardAvx2(nameof(BulkConvertNormalizedFloatToByte)); |
|
|
|
|
|
|
|
DebugGuard.IsTrue((source.Length % Vector<float>.Count) == 0, nameof(source), "source.Length should be divisable by Vector<float>.Count!"); |
|
|
|
|
|
|
|
if (source.Length == 0) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
ref Vector<float> srcBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source)); |
|
|
|
ref Octet.OfByte destBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(dest)); |
|
|
|
int n = source.Length / 8; |
|
|
|
|
|
|
|
Vector<float> magick = new Vector<float>(32768.0f); |
|
|
|
Vector<float> scale = new Vector<float>(255f) / new Vector<float>(256f); |
|
|
|
|
|
|
|
// need to copy to a temporary struct, because
|
|
|
|
// SimdUtils.Octet.OfUInt32 temp = Unsafe.As<Vector<float>, SimdUtils.Octet.OfUInt32>(ref x)
|
|
|
|
// does not work. TODO: This might be a CoreClr bug, need to ask/report
|
|
|
|
var temp = default(Octet.OfUInt32); |
|
|
|
ref Vector<float> tempRef = ref Unsafe.As<Octet.OfUInt32, Vector<float>>(ref temp); |
|
|
|
|
|
|
|
for (int i = 0; i < n; i++) |
|
|
|
{ |
|
|
|
// union { float f; uint32_t i; } u;
|
|
|
|
// u.f = 32768.0f + x * (255.0f / 256.0f);
|
|
|
|
// return (uint8_t)u.i;
|
|
|
|
Vector<float> x = Unsafe.Add(ref srcBase, i); |
|
|
|
x = (x * scale) + magick; |
|
|
|
tempRef = x; |
|
|
|
|
|
|
|
ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i); |
|
|
|
d.LoadFrom(ref temp); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |