mirror of https://github.com/SixLabors/ImageSharp
15 changed files with 344 additions and 425 deletions
@ -0,0 +1,77 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp; |
|||
|
|||
internal static partial class SimdUtils |
|||
{ |
|||
/// <summary>
|
|||
/// Converts all input <see cref="byte"/>-s to <see cref="float"/>-s normalized into [0..1].
|
|||
/// <paramref name="source"/> should be the of the same size as <paramref name="destination"/>,
|
|||
/// but there are no restrictions on the span's length.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span of bytes</param>
|
|||
/// <param name="destination">The destination span of floats</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
internal static void ByteToNormalizedFloat(ReadOnlySpan<byte> source, Span<float> destination) |
|||
{ |
|||
DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); |
|||
|
|||
HwIntrinsics.ByteToNormalizedFloatReduce(ref source, ref destination); |
|||
|
|||
if (source.Length > 0) |
|||
{ |
|||
ConvertByteToNormalizedFloatRemainder(source, destination); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert all <see cref="float"/> values normalized into [0..1] from 'source' into 'destination' buffer of <see cref="byte"/>.
|
|||
/// The values are scaled up into [0-255] and rounded, overflows are clamped.
|
|||
/// <paramref name="source"/> should be the of the same size as <paramref name="destination"/>,
|
|||
/// but there are no restrictions on the span's length.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span of floats</param>
|
|||
/// <param name="destination">The destination span of bytes</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
internal static void NormalizedFloatToByteSaturate(ReadOnlySpan<float> source, Span<byte> destination) |
|||
{ |
|||
DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); |
|||
|
|||
HwIntrinsics.NormalizedFloatToByteSaturateReduce(ref source, ref destination); |
|||
|
|||
if (source.Length > 0) |
|||
{ |
|||
ConvertNormalizedFloatToByteRemainder(source, destination); |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.NoInlining)] |
|||
private static void ConvertByteToNormalizedFloatRemainder(ReadOnlySpan<byte> source, Span<float> destination) |
|||
{ |
|||
ref byte sBase = ref MemoryMarshal.GetReference(source); |
|||
ref float dBase = ref MemoryMarshal.GetReference(destination); |
|||
|
|||
for (int i = 0; i < source.Length; i++) |
|||
{ |
|||
Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, i) / 255f; |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.NoInlining)] |
|||
private static void ConvertNormalizedFloatToByteRemainder(ReadOnlySpan<float> source, Span<byte> destination) |
|||
{ |
|||
ref float sBase = ref MemoryMarshal.GetReference(source); |
|||
ref byte dBase = ref MemoryMarshal.GetReference(destination); |
|||
for (int i = 0; i < source.Length; i++) |
|||
{ |
|||
Unsafe.Add(ref dBase, i) = ConvertToByte(Unsafe.Add(ref sBase, i)); |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static byte ConvertToByte(float f) => (byte)Numerics.Clamp((f * 255F) + 0.5F, 0, 255F); |
|||
} |
|||
@ -1,38 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
// ReSharper disable MemberHidesStaticFromOuterClass
|
|||
namespace SixLabors.ImageSharp; |
|||
|
|||
internal static partial class SimdUtils |
|||
{ |
|||
/// <summary>
|
|||
/// Implementation methods based on newer <see cref="Vector{T}"/> API-s (Vector.Widen, Vector.Narrow, Vector.ConvertTo*).
|
|||
/// Only accelerated only on RyuJIT having dotnet/coreclr#10662 merged (.NET Core 2.1+ .NET 4.7.2+)
|
|||
/// See:
|
|||
/// https://github.com/dotnet/coreclr/pull/10662
|
|||
/// API Proposal:
|
|||
/// https://github.com/dotnet/corefx/issues/15957
|
|||
/// </summary>
|
|||
public static class ExtendedIntrinsics |
|||
{ |
|||
public static bool IsAvailable { get; } = Vector.IsHardwareAccelerated; |
|||
|
|||
/// <summary>
|
|||
/// Widen and convert a vector of <see cref="short"/> values into 2 vectors of <see cref="float"/>-s.
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal static void ConvertToSingle( |
|||
Vector<short> source, |
|||
out Vector<float> dest1, |
|||
out Vector<float> dest2) |
|||
{ |
|||
Vector.Widen(source, out Vector<int> i1, out Vector<int> i2); |
|||
dest1 = Vector.ConvertToSingle(i1); |
|||
dest2 = Vector.ConvertToSingle(i2); |
|||
} |
|||
} |
|||
} |
|||
@ -1,83 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
// ReSharper disable MemberHidesStaticFromOuterClass
|
|||
namespace SixLabors.ImageSharp; |
|||
|
|||
internal static partial class SimdUtils |
|||
{ |
|||
/// <summary>
|
|||
/// Fallback implementation based on <see cref="Vector4"/> (128bit).
|
|||
/// For <see cref="Vector4"/>, efficient software fallback implementations are present,
|
|||
/// and we hope that even mono's JIT is able to emit SIMD instructions for that type :P
|
|||
/// </summary>
|
|||
public static class FallbackIntrinsics128 |
|||
{ |
|||
/// <summary>
|
|||
/// <see cref="ByteToNormalizedFloat"/> as many elements as possible, slicing them down (keeping the remainder).
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
internal static void ByteToNormalizedFloatReduce( |
|||
ref ReadOnlySpan<byte> source, |
|||
ref Span<float> dest) |
|||
{ |
|||
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); |
|||
|
|||
int remainder = Numerics.Modulo4(source.Length); |
|||
int adjustedCount = source.Length - remainder; |
|||
|
|||
if (adjustedCount > 0) |
|||
{ |
|||
ByteToNormalizedFloat(source[..adjustedCount], dest[..adjustedCount]); |
|||
|
|||
source = source[adjustedCount..]; |
|||
dest = dest[adjustedCount..]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Implementation of <see cref="SimdUtils.ByteToNormalizedFloat"/> using <see cref="Vector4"/>.
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ColdPath)] |
|||
internal static void ByteToNormalizedFloat(ReadOnlySpan<byte> source, Span<float> dest) |
|||
{ |
|||
DebugVerifySpanInput(source, dest, 4); |
|||
|
|||
uint count = (uint)dest.Length / 4; |
|||
if (count == 0) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
ref ByteVector4 sBase = ref Unsafe.As<byte, ByteVector4>(ref MemoryMarshal.GetReference(source)); |
|||
ref Vector4 dBase = ref Unsafe.As<float, Vector4>(ref MemoryMarshal.GetReference(dest)); |
|||
|
|||
const float scale = 1f / 255f; |
|||
Vector4 d = default; |
|||
|
|||
for (nuint i = 0; i < count; i++) |
|||
{ |
|||
ref ByteVector4 s = ref Unsafe.Add(ref sBase, i); |
|||
d.X = s.X; |
|||
d.Y = s.Y; |
|||
d.Z = s.Z; |
|||
d.W = s.W; |
|||
d *= scale; |
|||
Unsafe.Add(ref dBase, i) = d; |
|||
} |
|||
} |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
private struct ByteVector4 |
|||
{ |
|||
public byte X; |
|||
public byte Y; |
|||
public byte Z; |
|||
public byte W; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue