mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Introduces `RgbaHalf` and `RgbaHalfP` plus bulk pixel-operation support, updates AOT seeding, and routes EXR decoding through the new scaled conversions. Also fixes scaled/native range handling for half, SNORM, and signed integer pixel formats, and expands tests for the new layouts and edge cases.pull/3154/head
51 changed files with 2656 additions and 988 deletions
@ -0,0 +1,72 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Provides optimized overrides for bulk operations.
|
||||
|
/// </content>
|
||||
|
public partial struct RgbaHalf |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides optimized bulk operations for <see cref="RgbaHalf"/>.
|
||||
|
/// </summary>
|
||||
|
internal class PixelOperations : PixelOperations<RgbaHalf> |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan<RgbaHalf> source, Span<Vector4> destination) |
||||
|
{ |
||||
|
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); |
||||
|
|
||||
|
// RgbaHalf and RgbaHalfP have the same four-half layout. Sharing the binary16 expansion kernel keeps this path
|
||||
|
// vectorized without changing the unassociated meaning of the source components.
|
||||
|
RgbaHalfP.PixelOperations.Unpack(MemoryMarshal.Cast<RgbaHalf, RgbaHalfP>(source), destination[..source.Length]); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan<RgbaHalf> source, Span<Vector4> destination) |
||||
|
{ |
||||
|
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); |
||||
|
|
||||
|
// Association is fused with half expansion so the conversion reads and writes each span once.
|
||||
|
RgbaHalfP.PixelOperations.UnpackAssociated(MemoryMarshal.Cast<RgbaHalf, RgbaHalfP>(source), destination[..source.Length]); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan<RgbaHalf> source, Span<Vector4> destination) |
||||
|
=> this.ToUnassociatedVector4(configuration, source, destination); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan<RgbaHalf> source, Span<Vector4> destination) |
||||
|
=> this.ToAssociatedVector4(configuration, source, destination); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span<Vector4> source, Span<RgbaHalf> destination) |
||||
|
{ |
||||
|
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); |
||||
|
|
||||
|
// The layouts are identical, so the shared packer can write RgbaHalf storage without an intermediate buffer.
|
||||
|
RgbaHalfP.PixelOperations.Pack(source, MemoryMarshal.Cast<RgbaHalf, RgbaHalfP>(destination[..source.Length])); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void FromAssociatedVector4Destructive(Configuration configuration, Span<Vector4> source, Span<RgbaHalf> destination) |
||||
|
{ |
||||
|
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); |
||||
|
|
||||
|
// Unassociation is fused with binary16 packing so processors do not pay for another pass over their vector buffer.
|
||||
|
RgbaHalfP.PixelOperations.PackFromAssociated(source, MemoryMarshal.Cast<RgbaHalf, RgbaHalfP>(destination[..source.Length])); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<RgbaHalf> destination) |
||||
|
=> this.FromUnassociatedVector4Destructive(configuration, source, destination); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<RgbaHalf> destination) |
||||
|
=> this.FromAssociatedVector4Destructive(configuration, source, destination); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,795 @@ |
|||||
|
// 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 SixLabors.ImageSharp.Common.Helpers; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Provides optimized overrides for bulk operations.
|
||||
|
/// </content>
|
||||
|
public partial struct RgbaHalfP |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides optimized bulk operations for <see cref="RgbaHalfP"/>.
|
||||
|
/// </summary>
|
||||
|
internal class PixelOperations : AssociatedAlphaPixelOperations<RgbaHalfP> |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan<RgbaHalfP> source, Span<Vector4> destination) |
||||
|
{ |
||||
|
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); |
||||
|
|
||||
|
UnpackUnassociated(source, destination[..source.Length]); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan<RgbaHalfP> source, Span<Vector4> destination) |
||||
|
{ |
||||
|
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); |
||||
|
|
||||
|
Unpack(source, destination[..source.Length]); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan<RgbaHalfP> source, Span<Vector4> destination) |
||||
|
=> this.ToUnassociatedVector4(configuration, source, destination); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan<RgbaHalfP> source, Span<Vector4> destination) |
||||
|
=> this.ToAssociatedVector4(configuration, source, destination); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span<Vector4> source, Span<RgbaHalfP> destination) |
||||
|
{ |
||||
|
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); |
||||
|
|
||||
|
PackUnassociated(source, destination[..source.Length]); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void FromAssociatedVector4Destructive(Configuration configuration, Span<Vector4> source, Span<RgbaHalfP> destination) |
||||
|
{ |
||||
|
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); |
||||
|
|
||||
|
PackAssociated(source, destination[..source.Length]); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<RgbaHalfP> destination) |
||||
|
=> this.FromUnassociatedVector4Destructive(configuration, source, destination); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<RgbaHalfP> destination) |
||||
|
=> this.FromAssociatedVector4Destructive(configuration, source, destination); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Expands binary16 components without changing their unit-range representation.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The packed source pixels.</param>
|
||||
|
/// <param name="destination">The destination vectors.</param>
|
||||
|
internal static void Unpack(ReadOnlySpan<RgbaHalfP> source, Span<Vector4> destination) |
||||
|
{ |
||||
|
ref ushort sourceBase = ref Unsafe.As<RgbaHalfP, ushort>(ref MemoryMarshal.GetReference(source)); |
||||
|
ref float destinationBase = ref Unsafe.As<Vector4, float>(ref MemoryMarshal.GetReference(destination)); |
||||
|
int componentCount = source.Length * Vector128<float>.Count; |
||||
|
int i = 0; |
||||
|
|
||||
|
if (Vector512.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector512<ushort>.Count; i += Vector512<ushort>.Count) |
||||
|
{ |
||||
|
Vector512<ushort> packed = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); |
||||
|
(Vector512<float> lower, Vector512<float> upper) = HalfTypeHelper.Unpack(packed); |
||||
|
Vector512.StoreUnsafe(lower, ref destinationBase, (nuint)i); |
||||
|
Vector512.StoreUnsafe(upper, ref destinationBase, (nuint)(i + Vector512<float>.Count)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector256.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector256<ushort>.Count; i += Vector256<ushort>.Count) |
||||
|
{ |
||||
|
Vector256<ushort> packed = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); |
||||
|
(Vector256<float> lower, Vector256<float> upper) = HalfTypeHelper.Unpack(packed); |
||||
|
Vector256.StoreUnsafe(lower, ref destinationBase, (nuint)i); |
||||
|
Vector256.StoreUnsafe(upper, ref destinationBase, (nuint)(i + Vector256<float>.Count)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector128.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector128<ushort>.Count; i += Vector128<ushort>.Count) |
||||
|
{ |
||||
|
Vector128<ushort> packed = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); |
||||
|
(Vector128<float> lower, Vector128<float> upper) = HalfTypeHelper.Unpack(packed); |
||||
|
Vector128.StoreUnsafe(lower, ref destinationBase, (nuint)i); |
||||
|
Vector128.StoreUnsafe(upper, ref destinationBase, (nuint)(i + Vector128<float>.Count)); |
||||
|
} |
||||
|
|
||||
|
if (i < componentCount) |
||||
|
{ |
||||
|
// Four binary16 components form one pixel, so the only possible remainder is one complete pixel.
|
||||
|
ulong remainder = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<ushort, byte>(ref Unsafe.Add(ref sourceBase, (uint)i))); |
||||
|
Vector128<ushort> packed = Vector128.CreateScalarUnsafe(remainder).AsUInt16(); |
||||
|
Vector128.StoreUnsafe(HalfTypeHelper.Unpack(packed).Lower, ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ref RgbaHalfP pixelBase = ref Unsafe.As<ushort, RgbaHalfP>(ref sourceBase); |
||||
|
ref Vector4 vectorBase = ref Unsafe.As<float, Vector4>(ref destinationBase); |
||||
|
|
||||
|
for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) |
||||
|
{ |
||||
|
Unsafe.Add(ref vectorBase, (uint)pixelIndex) = Unsafe.Add(ref pixelBase, (uint)pixelIndex).ToVector4(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Expands unassociated binary16 components and associates RGB in the same pass.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The packed source pixels.</param>
|
||||
|
/// <param name="destination">The destination vectors.</param>
|
||||
|
internal static void UnpackAssociated(ReadOnlySpan<RgbaHalfP> source, Span<Vector4> destination) |
||||
|
{ |
||||
|
ref ushort sourceBase = ref Unsafe.As<RgbaHalfP, ushort>(ref MemoryMarshal.GetReference(source)); |
||||
|
ref float destinationBase = ref Unsafe.As<Vector4, float>(ref MemoryMarshal.GetReference(destination)); |
||||
|
int componentCount = source.Length * Vector128<float>.Count; |
||||
|
int i = 0; |
||||
|
|
||||
|
if (Vector512.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector512<ushort>.Count; i += Vector512<ushort>.Count) |
||||
|
{ |
||||
|
Vector512<ushort> packed = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); |
||||
|
(Vector512<float> lower, Vector512<float> upper) = HalfTypeHelper.Unpack(packed); |
||||
|
Vector512.StoreUnsafe(Associate(lower), ref destinationBase, (nuint)i); |
||||
|
Vector512.StoreUnsafe(Associate(upper), ref destinationBase, (nuint)(i + Vector512<float>.Count)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector256.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector256<ushort>.Count; i += Vector256<ushort>.Count) |
||||
|
{ |
||||
|
Vector256<ushort> packed = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); |
||||
|
(Vector256<float> lower, Vector256<float> upper) = HalfTypeHelper.Unpack(packed); |
||||
|
Vector256.StoreUnsafe(Associate(lower), ref destinationBase, (nuint)i); |
||||
|
Vector256.StoreUnsafe(Associate(upper), ref destinationBase, (nuint)(i + Vector256<float>.Count)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector128.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector128<ushort>.Count; i += Vector128<ushort>.Count) |
||||
|
{ |
||||
|
Vector128<ushort> packed = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); |
||||
|
(Vector128<float> lower, Vector128<float> upper) = HalfTypeHelper.Unpack(packed); |
||||
|
Vector128.StoreUnsafe(Associate(lower), ref destinationBase, (nuint)i); |
||||
|
Vector128.StoreUnsafe(Associate(upper), ref destinationBase, (nuint)(i + Vector128<float>.Count)); |
||||
|
} |
||||
|
|
||||
|
if (i < componentCount) |
||||
|
{ |
||||
|
// Four binary16 components form one pixel, so the only possible remainder is one complete pixel.
|
||||
|
ulong remainder = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<ushort, byte>(ref Unsafe.Add(ref sourceBase, (uint)i))); |
||||
|
Vector128<ushort> packed = Vector128.CreateScalarUnsafe(remainder).AsUInt16(); |
||||
|
Vector128.StoreUnsafe(Associate(HalfTypeHelper.Unpack(packed).Lower), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ref RgbaHalfP pixelBase = ref Unsafe.As<ushort, RgbaHalfP>(ref sourceBase); |
||||
|
ref Vector4 vectorBase = ref Unsafe.As<float, Vector4>(ref destinationBase); |
||||
|
|
||||
|
for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) |
||||
|
{ |
||||
|
Vector4 vector = Unsafe.Add(ref pixelBase, (uint)pixelIndex).ToVector4(); |
||||
|
Numerics.Premultiply(ref vector); |
||||
|
Unsafe.Add(ref vectorBase, (uint)pixelIndex) = vector; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Expands associated binary16 components and unassociates RGB in the same pass.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The packed source pixels.</param>
|
||||
|
/// <param name="destination">The destination vectors.</param>
|
||||
|
internal static void UnpackUnassociated(ReadOnlySpan<RgbaHalfP> source, Span<Vector4> destination) |
||||
|
{ |
||||
|
ref ushort sourceBase = ref Unsafe.As<RgbaHalfP, ushort>(ref MemoryMarshal.GetReference(source)); |
||||
|
ref float destinationBase = ref Unsafe.As<Vector4, float>(ref MemoryMarshal.GetReference(destination)); |
||||
|
int componentCount = source.Length * Vector128<float>.Count; |
||||
|
int i = 0; |
||||
|
|
||||
|
if (Vector512.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector512<ushort>.Count; i += Vector512<ushort>.Count) |
||||
|
{ |
||||
|
Vector512<ushort> packed = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); |
||||
|
(Vector512<float> lower, Vector512<float> upper) = HalfTypeHelper.Unpack(packed); |
||||
|
Vector512.StoreUnsafe(Unassociate(lower), ref destinationBase, (nuint)i); |
||||
|
Vector512.StoreUnsafe(Unassociate(upper), ref destinationBase, (nuint)(i + Vector512<float>.Count)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector256.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector256<ushort>.Count; i += Vector256<ushort>.Count) |
||||
|
{ |
||||
|
Vector256<ushort> packed = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); |
||||
|
(Vector256<float> lower, Vector256<float> upper) = HalfTypeHelper.Unpack(packed); |
||||
|
Vector256.StoreUnsafe(Unassociate(lower), ref destinationBase, (nuint)i); |
||||
|
Vector256.StoreUnsafe(Unassociate(upper), ref destinationBase, (nuint)(i + Vector256<float>.Count)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector128.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector128<ushort>.Count; i += Vector128<ushort>.Count) |
||||
|
{ |
||||
|
Vector128<ushort> packed = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); |
||||
|
(Vector128<float> lower, Vector128<float> upper) = HalfTypeHelper.Unpack(packed); |
||||
|
Vector128.StoreUnsafe(Unassociate(lower), ref destinationBase, (nuint)i); |
||||
|
Vector128.StoreUnsafe(Unassociate(upper), ref destinationBase, (nuint)(i + Vector128<float>.Count)); |
||||
|
} |
||||
|
|
||||
|
if (i < componentCount) |
||||
|
{ |
||||
|
// Four binary16 components form one pixel, so the only possible remainder is one complete pixel.
|
||||
|
ulong remainder = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<ushort, byte>(ref Unsafe.Add(ref sourceBase, (uint)i))); |
||||
|
Vector128<ushort> packed = Vector128.CreateScalarUnsafe(remainder).AsUInt16(); |
||||
|
Vector128.StoreUnsafe(Unassociate(HalfTypeHelper.Unpack(packed).Lower), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ref RgbaHalfP pixelBase = ref Unsafe.As<ushort, RgbaHalfP>(ref sourceBase); |
||||
|
ref Vector4 vectorBase = ref Unsafe.As<float, Vector4>(ref destinationBase); |
||||
|
|
||||
|
for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) |
||||
|
{ |
||||
|
Unsafe.Add(ref vectorBase, (uint)pixelIndex) = Unsafe.Add(ref pixelBase, (uint)pixelIndex).ToUnassociatedVector4(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Packs unassociated unit-range vectors directly into binary16 storage.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The source vectors.</param>
|
||||
|
/// <param name="destination">The destination pixels.</param>
|
||||
|
internal static void Pack(Span<Vector4> source, Span<RgbaHalfP> destination) |
||||
|
{ |
||||
|
ref float sourceBase = ref Unsafe.As<Vector4, float>(ref MemoryMarshal.GetReference(source)); |
||||
|
ref ushort destinationBase = ref Unsafe.As<RgbaHalfP, ushort>(ref MemoryMarshal.GetReference(destination)); |
||||
|
int componentCount = source.Length * Vector128<float>.Count; |
||||
|
int i = 0; |
||||
|
|
||||
|
if (Vector512.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector512<ushort>.Count; i += Vector512<ushort>.Count) |
||||
|
{ |
||||
|
Vector512<float> lower = ClampUnit(Vector512.LoadUnsafe(ref sourceBase, (nuint)i)); |
||||
|
Vector512<float> upper = ClampUnit(Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + Vector512<float>.Count))); |
||||
|
Vector512.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector256.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector256<ushort>.Count; i += Vector256<ushort>.Count) |
||||
|
{ |
||||
|
Vector256<float> lower = ClampUnit(Vector256.LoadUnsafe(ref sourceBase, (nuint)i)); |
||||
|
Vector256<float> upper = ClampUnit(Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + Vector256<float>.Count))); |
||||
|
Vector256.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector128.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector128<ushort>.Count; i += Vector128<ushort>.Count) |
||||
|
{ |
||||
|
Vector128<float> lower = ClampUnit(Vector128.LoadUnsafe(ref sourceBase, (nuint)i)); |
||||
|
Vector128<float> upper = ClampUnit(Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + Vector128<float>.Count))); |
||||
|
Vector128.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
|
||||
|
if (i < componentCount) |
||||
|
{ |
||||
|
// Duplicate the final vector to use the two-input narrowing primitive, then store only one complete pixel.
|
||||
|
Vector128<float> vector = ClampUnit(Vector128.LoadUnsafe(ref sourceBase, (nuint)i)); |
||||
|
Vector128<ushort> packed = HalfTypeHelper.Pack(vector, vector); |
||||
|
Unsafe.WriteUnaligned(ref Unsafe.As<ushort, byte>(ref Unsafe.Add(ref destinationBase, (uint)i)), packed.AsUInt64().GetElement(0)); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ref Vector4 vectorBase = ref Unsafe.As<float, Vector4>(ref sourceBase); |
||||
|
ref RgbaHalfP pixelBase = ref Unsafe.As<ushort, RgbaHalfP>(ref destinationBase); |
||||
|
|
||||
|
for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) |
||||
|
{ |
||||
|
Vector4 vector = Numerics.Clamp(Unsafe.Add(ref vectorBase, (uint)pixelIndex), Vector4.Zero, Vector4.One); |
||||
|
Unsafe.Add(ref pixelBase, (uint)pixelIndex) = new RgbaHalfP(vector.X, vector.Y, vector.Z, vector.W); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Packs vectors directly into IEEE 754 binary16 storage without applying unit-range color constraints.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The source vectors.</param>
|
||||
|
/// <param name="destination">The destination pixels.</param>
|
||||
|
internal static void PackUnclamped(Span<Vector4> source, Span<RgbaHalfP> destination) |
||||
|
{ |
||||
|
ref float sourceBase = ref Unsafe.As<Vector4, float>(ref MemoryMarshal.GetReference(source)); |
||||
|
ref ushort destinationBase = ref Unsafe.As<RgbaHalfP, ushort>(ref MemoryMarshal.GetReference(destination)); |
||||
|
int componentCount = source.Length * Vector128<float>.Count; |
||||
|
int i = 0; |
||||
|
|
||||
|
if (Vector512.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector512<ushort>.Count; i += Vector512<ushort>.Count) |
||||
|
{ |
||||
|
Vector512<float> lower = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); |
||||
|
Vector512<float> upper = Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + Vector512<float>.Count)); |
||||
|
Vector512.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector256.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector256<ushort>.Count; i += Vector256<ushort>.Count) |
||||
|
{ |
||||
|
Vector256<float> lower = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); |
||||
|
Vector256<float> upper = Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + Vector256<float>.Count)); |
||||
|
Vector256.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector128.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector128<ushort>.Count; i += Vector128<ushort>.Count) |
||||
|
{ |
||||
|
Vector128<float> lower = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); |
||||
|
Vector128<float> upper = Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + Vector128<float>.Count)); |
||||
|
Vector128.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
|
||||
|
if (i < componentCount) |
||||
|
{ |
||||
|
// Duplicate the final vector to use the two-input narrowing primitive, then store only one complete pixel.
|
||||
|
Vector128<float> vector = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); |
||||
|
Vector128<ushort> packed = HalfTypeHelper.Pack(vector, vector); |
||||
|
Unsafe.WriteUnaligned(ref Unsafe.As<ushort, byte>(ref Unsafe.Add(ref destinationBase, (uint)i)), packed.AsUInt64().GetElement(0)); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ref Vector4 vectorBase = ref Unsafe.As<float, Vector4>(ref sourceBase); |
||||
|
ref RgbaHalfP pixelBase = ref Unsafe.As<ushort, RgbaHalfP>(ref destinationBase); |
||||
|
|
||||
|
for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) |
||||
|
{ |
||||
|
Vector4 vector = Unsafe.Add(ref vectorBase, (uint)pixelIndex); |
||||
|
Unsafe.Add(ref pixelBase, (uint)pixelIndex) = new RgbaHalfP(vector.X, vector.Y, vector.Z, vector.W); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Unassociates vectors and packs unassociated unit-range binary16 storage in the same pass.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The associated source vectors.</param>
|
||||
|
/// <param name="destination">The destination pixels.</param>
|
||||
|
internal static void PackFromAssociated(Span<Vector4> source, Span<RgbaHalfP> destination) |
||||
|
{ |
||||
|
ref float sourceBase = ref Unsafe.As<Vector4, float>(ref MemoryMarshal.GetReference(source)); |
||||
|
ref ushort destinationBase = ref Unsafe.As<RgbaHalfP, ushort>(ref MemoryMarshal.GetReference(destination)); |
||||
|
int componentCount = source.Length * Vector128<float>.Count; |
||||
|
int i = 0; |
||||
|
|
||||
|
if (Vector512.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector512<ushort>.Count; i += Vector512<ushort>.Count) |
||||
|
{ |
||||
|
Vector512<float> lower = ClampUnit(Unassociate(Vector512.LoadUnsafe(ref sourceBase, (nuint)i))); |
||||
|
Vector512<float> upper = ClampUnit(Unassociate(Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + Vector512<float>.Count)))); |
||||
|
Vector512.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector256.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector256<ushort>.Count; i += Vector256<ushort>.Count) |
||||
|
{ |
||||
|
Vector256<float> lower = ClampUnit(Unassociate(Vector256.LoadUnsafe(ref sourceBase, (nuint)i))); |
||||
|
Vector256<float> upper = ClampUnit(Unassociate(Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + Vector256<float>.Count)))); |
||||
|
Vector256.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector128.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector128<ushort>.Count; i += Vector128<ushort>.Count) |
||||
|
{ |
||||
|
Vector128<float> lower = ClampUnit(Unassociate(Vector128.LoadUnsafe(ref sourceBase, (nuint)i))); |
||||
|
Vector128<float> upper = ClampUnit(Unassociate(Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + Vector128<float>.Count)))); |
||||
|
Vector128.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
|
||||
|
if (i < componentCount) |
||||
|
{ |
||||
|
// Duplicate the final vector to use the two-input narrowing primitive, then store only one complete pixel.
|
||||
|
Vector128<float> vector = ClampUnit(Unassociate(Vector128.LoadUnsafe(ref sourceBase, (nuint)i))); |
||||
|
Vector128<ushort> packed = HalfTypeHelper.Pack(vector, vector); |
||||
|
Unsafe.WriteUnaligned(ref Unsafe.As<ushort, byte>(ref Unsafe.Add(ref destinationBase, (uint)i)), packed.AsUInt64().GetElement(0)); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ref Vector4 vectorBase = ref Unsafe.As<float, Vector4>(ref sourceBase); |
||||
|
ref RgbaHalfP pixelBase = ref Unsafe.As<ushort, RgbaHalfP>(ref destinationBase); |
||||
|
|
||||
|
for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) |
||||
|
{ |
||||
|
Vector4 vector = Unsafe.Add(ref vectorBase, (uint)pixelIndex); |
||||
|
Numerics.UnPremultiply(ref vector); |
||||
|
vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); |
||||
|
Unsafe.Add(ref pixelBase, (uint)pixelIndex) = new RgbaHalfP(vector.X, vector.Y, vector.Z, vector.W); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Associates unassociated vectors with their stored binary16 alpha and packs them in one pass.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The unassociated source vectors.</param>
|
||||
|
/// <param name="destination">The destination pixels.</param>
|
||||
|
internal static void PackUnassociated(Span<Vector4> source, Span<RgbaHalfP> destination) |
||||
|
{ |
||||
|
ref float sourceBase = ref Unsafe.As<Vector4, float>(ref MemoryMarshal.GetReference(source)); |
||||
|
ref ushort destinationBase = ref Unsafe.As<RgbaHalfP, ushort>(ref MemoryMarshal.GetReference(destination)); |
||||
|
int componentCount = source.Length * Vector128<float>.Count; |
||||
|
int i = 0; |
||||
|
|
||||
|
// Alpha is rounded to the exact binary16 value that will be stored before RGB is associated with it.
|
||||
|
if (Vector512.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector512<ushort>.Count; i += Vector512<ushort>.Count) |
||||
|
{ |
||||
|
Vector512<float> lower = AssociateForStorage(Vector512.LoadUnsafe(ref sourceBase, (nuint)i)); |
||||
|
Vector512<float> upper = AssociateForStorage(Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + Vector512<float>.Count))); |
||||
|
Vector512.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector256.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector256<ushort>.Count; i += Vector256<ushort>.Count) |
||||
|
{ |
||||
|
Vector256<float> lower = AssociateForStorage(Vector256.LoadUnsafe(ref sourceBase, (nuint)i)); |
||||
|
Vector256<float> upper = AssociateForStorage(Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + Vector256<float>.Count))); |
||||
|
Vector256.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector128.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector128<ushort>.Count; i += Vector128<ushort>.Count) |
||||
|
{ |
||||
|
Vector128<float> lower = AssociateForStorage(Vector128.LoadUnsafe(ref sourceBase, (nuint)i)); |
||||
|
Vector128<float> upper = AssociateForStorage(Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + Vector128<float>.Count))); |
||||
|
Vector128.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
|
||||
|
if (i < componentCount) |
||||
|
{ |
||||
|
// Duplicate the final vector to use the two-input narrowing primitive, then store only one complete pixel.
|
||||
|
Vector128<float> vector = AssociateForStorage(Vector128.LoadUnsafe(ref sourceBase, (nuint)i)); |
||||
|
Vector128<ushort> packed = HalfTypeHelper.Pack(vector, vector); |
||||
|
Unsafe.WriteUnaligned(ref Unsafe.As<ushort, byte>(ref Unsafe.Add(ref destinationBase, (uint)i)), packed.AsUInt64().GetElement(0)); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ref Vector4 vectorBase = ref Unsafe.As<float, Vector4>(ref sourceBase); |
||||
|
ref RgbaHalfP pixelBase = ref Unsafe.As<ushort, RgbaHalfP>(ref destinationBase); |
||||
|
|
||||
|
for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) |
||||
|
{ |
||||
|
Unsafe.Add(ref pixelBase, (uint)pixelIndex) = RgbaHalfP.FromUnassociatedVector4(Unsafe.Add(ref vectorBase, (uint)pixelIndex)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reassociates vectors with their stored binary16 alpha and packs them in one pass.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The associated source vectors.</param>
|
||||
|
/// <param name="destination">The destination pixels.</param>
|
||||
|
internal static void PackAssociated(Span<Vector4> source, Span<RgbaHalfP> destination) |
||||
|
{ |
||||
|
ref float sourceBase = ref Unsafe.As<Vector4, float>(ref MemoryMarshal.GetReference(source)); |
||||
|
ref ushort destinationBase = ref Unsafe.As<RgbaHalfP, ushort>(ref MemoryMarshal.GetReference(destination)); |
||||
|
int componentCount = source.Length * Vector128<float>.Count; |
||||
|
int i = 0; |
||||
|
|
||||
|
// Scaling RGB by storedAlpha / inputAlpha preserves straight color when binary16 rounds the alpha channel.
|
||||
|
if (Vector512.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector512<ushort>.Count; i += Vector512<ushort>.Count) |
||||
|
{ |
||||
|
Vector512<float> lower = ReassociateForStorage(Vector512.LoadUnsafe(ref sourceBase, (nuint)i)); |
||||
|
Vector512<float> upper = ReassociateForStorage(Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + Vector512<float>.Count))); |
||||
|
Vector512.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector256.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector256<ushort>.Count; i += Vector256<ushort>.Count) |
||||
|
{ |
||||
|
Vector256<float> lower = ReassociateForStorage(Vector256.LoadUnsafe(ref sourceBase, (nuint)i)); |
||||
|
Vector256<float> upper = ReassociateForStorage(Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + Vector256<float>.Count))); |
||||
|
Vector256.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector128.IsHardwareAccelerated) |
||||
|
{ |
||||
|
for (; i <= componentCount - Vector128<ushort>.Count; i += Vector128<ushort>.Count) |
||||
|
{ |
||||
|
Vector128<float> lower = ReassociateForStorage(Vector128.LoadUnsafe(ref sourceBase, (nuint)i)); |
||||
|
Vector128<float> upper = ReassociateForStorage(Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + Vector128<float>.Count))); |
||||
|
Vector128.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); |
||||
|
} |
||||
|
|
||||
|
if (i < componentCount) |
||||
|
{ |
||||
|
// Duplicate the final vector to use the two-input narrowing primitive, then store only one complete pixel.
|
||||
|
Vector128<float> vector = ReassociateForStorage(Vector128.LoadUnsafe(ref sourceBase, (nuint)i)); |
||||
|
Vector128<ushort> packed = HalfTypeHelper.Pack(vector, vector); |
||||
|
Unsafe.WriteUnaligned(ref Unsafe.As<ushort, byte>(ref Unsafe.Add(ref destinationBase, (uint)i)), packed.AsUInt64().GetElement(0)); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ref Vector4 vectorBase = ref Unsafe.As<float, Vector4>(ref sourceBase); |
||||
|
ref RgbaHalfP pixelBase = ref Unsafe.As<ushort, RgbaHalfP>(ref destinationBase); |
||||
|
|
||||
|
for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) |
||||
|
{ |
||||
|
Unsafe.Add(ref pixelBase, (uint)pixelIndex) = RgbaHalfP.FromAssociatedVector4(Unsafe.Add(ref vectorBase, (uint)pixelIndex)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Associates RGB with alpha while preserving each alpha lane.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The unassociated vectors.</param>
|
||||
|
/// <returns>The associated vectors.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector128<float> Associate(Vector128<float> source) |
||||
|
{ |
||||
|
Vector128<float> alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); |
||||
|
Vector128<float> result = source * alpha; |
||||
|
return Vector128.ConditionalSelect(Vector128.Create(0, 0, 0, -1).AsSingle(), alpha, result); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Associates RGB with alpha while preserving each alpha lane.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The unassociated vectors.</param>
|
||||
|
/// <returns>The associated vectors.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector256<float> Associate(Vector256<float> source) |
||||
|
{ |
||||
|
Vector256<float> alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); |
||||
|
Vector256<float> result = source * alpha; |
||||
|
return Vector256.ConditionalSelect(Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(), alpha, result); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Associates RGB with alpha while preserving each alpha lane.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The unassociated vectors.</param>
|
||||
|
/// <returns>The associated vectors.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector512<float> Associate(Vector512<float> source) |
||||
|
{ |
||||
|
Vector512<float> alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); |
||||
|
Vector512<float> result = source * alpha; |
||||
|
Vector512<float> alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); |
||||
|
return Vector512.ConditionalSelect(alphaMask, alpha, result); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Unassociates RGB while preserving each alpha lane.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The associated vectors.</param>
|
||||
|
/// <returns>The unassociated vectors.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector128<float> Unassociate(Vector128<float> source) |
||||
|
{ |
||||
|
Vector128<float> alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); |
||||
|
return Numerics.UnPremultiply(source, alpha); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Unassociates RGB while preserving each alpha lane.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The associated vectors.</param>
|
||||
|
/// <returns>The unassociated vectors.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector256<float> Unassociate(Vector256<float> source) |
||||
|
{ |
||||
|
Vector256<float> alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); |
||||
|
return Numerics.UnPremultiply(source, alpha); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Unassociates RGB while preserving each alpha lane.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The associated vectors.</param>
|
||||
|
/// <returns>The unassociated vectors.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector512<float> Unassociate(Vector512<float> source) |
||||
|
{ |
||||
|
Vector512<float> alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); |
||||
|
return Numerics.UnPremultiply(source, alpha); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clamps vectors to the unit range represented by the pixel format.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The vectors to clamp.</param>
|
||||
|
/// <returns>The clamped vectors.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector128<float> ClampUnit(Vector128<float> source) |
||||
|
{ |
||||
|
Vector128<float> clamped = Vector128.Min(Vector128.Max(source, Vector128<float>.Zero), Vector128<float>.One); |
||||
|
|
||||
|
// Ordered comparison is false for NaN, restoring the source lane to match the scalar clamp contract.
|
||||
|
return Vector128.ConditionalSelect(Vector128.Equals(source, source), clamped, source); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clamps vectors to the unit range represented by the pixel format.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The vectors to clamp.</param>
|
||||
|
/// <returns>The clamped vectors.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector256<float> ClampUnit(Vector256<float> source) |
||||
|
{ |
||||
|
Vector256<float> clamped = Vector256.Min(Vector256.Max(source, Vector256<float>.Zero), Vector256<float>.One); |
||||
|
|
||||
|
// Ordered comparison is false for NaN, restoring the source lane to match the scalar clamp contract.
|
||||
|
return Vector256.ConditionalSelect(Vector256.Equals(source, source), clamped, source); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clamps vectors to the unit range represented by the pixel format.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The vectors to clamp.</param>
|
||||
|
/// <returns>The clamped vectors.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector512<float> ClampUnit(Vector512<float> source) |
||||
|
{ |
||||
|
Vector512<float> clamped = Vector512.Min(Vector512.Max(source, Vector512<float>.Zero), Vector512<float>.One); |
||||
|
|
||||
|
// Ordered comparison is false for NaN, restoring the source lane to match the scalar clamp contract.
|
||||
|
return Vector512.ConditionalSelect(Vector512.Equals(source, source), clamped, source); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Associates unassociated vectors with the alpha value binary16 storage can reproduce.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The unassociated vectors.</param>
|
||||
|
/// <returns>The associated vectors ready for packing.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector128<float> AssociateForStorage(Vector128<float> source) |
||||
|
{ |
||||
|
source = ClampUnit(source); |
||||
|
Vector128<float> alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); |
||||
|
Vector128<float> storedAlpha = HalfTypeHelper.RoundToHalf(alpha); |
||||
|
Vector128<float> result = source * storedAlpha; |
||||
|
return Vector128.ConditionalSelect(Vector128.Create(0, 0, 0, -1).AsSingle(), storedAlpha, result); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Associates unassociated vectors with the alpha value binary16 storage can reproduce.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The unassociated vectors.</param>
|
||||
|
/// <returns>The associated vectors ready for packing.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector256<float> AssociateForStorage(Vector256<float> source) |
||||
|
{ |
||||
|
source = ClampUnit(source); |
||||
|
Vector256<float> alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); |
||||
|
Vector256<float> storedAlpha = HalfTypeHelper.RoundToHalf(alpha); |
||||
|
Vector256<float> result = source * storedAlpha; |
||||
|
return Vector256.ConditionalSelect(Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(), storedAlpha, result); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Associates unassociated vectors with the alpha value binary16 storage can reproduce.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The unassociated vectors.</param>
|
||||
|
/// <returns>The associated vectors ready for packing.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector512<float> AssociateForStorage(Vector512<float> source) |
||||
|
{ |
||||
|
source = ClampUnit(source); |
||||
|
Vector512<float> alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); |
||||
|
Vector512<float> storedAlpha = HalfTypeHelper.RoundToHalf(alpha); |
||||
|
Vector512<float> result = source * storedAlpha; |
||||
|
Vector512<float> alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); |
||||
|
return Vector512.ConditionalSelect(alphaMask, storedAlpha, result); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reassociates vectors with the alpha value binary16 storage can reproduce.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The associated vectors.</param>
|
||||
|
/// <returns>The associated vectors ready for packing.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector128<float> ReassociateForStorage(Vector128<float> source) |
||||
|
{ |
||||
|
Vector128<float> zero = Vector128<float>.Zero; |
||||
|
Vector128<float> alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); |
||||
|
Vector128<float> clampedAlpha = ClampUnit(alpha); |
||||
|
Vector128<float> storedAlpha = HalfTypeHelper.RoundToHalf(clampedAlpha); |
||||
|
Vector128<float> result = source * (storedAlpha / alpha); |
||||
|
result = Vector128.ConditionalSelect(Vector128.Create(0, 0, 0, -1).AsSingle(), storedAlpha, result); |
||||
|
result = Vector128.Min(Vector128.Max(result, zero), storedAlpha); |
||||
|
return Vector128.ConditionalSelect(Vector128.LessThanOrEqual(alpha, zero), zero, result); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reassociates vectors with the alpha value binary16 storage can reproduce.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The associated vectors.</param>
|
||||
|
/// <returns>The associated vectors ready for packing.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector256<float> ReassociateForStorage(Vector256<float> source) |
||||
|
{ |
||||
|
Vector256<float> zero = Vector256<float>.Zero; |
||||
|
Vector256<float> alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); |
||||
|
Vector256<float> clampedAlpha = ClampUnit(alpha); |
||||
|
Vector256<float> storedAlpha = HalfTypeHelper.RoundToHalf(clampedAlpha); |
||||
|
Vector256<float> result = source * (storedAlpha / alpha); |
||||
|
result = Vector256.ConditionalSelect(Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(), storedAlpha, result); |
||||
|
result = Vector256.Min(Vector256.Max(result, zero), storedAlpha); |
||||
|
return Vector256.ConditionalSelect(Vector256.LessThanOrEqual(alpha, zero), zero, result); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reassociates vectors with the alpha value binary16 storage can reproduce.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The associated vectors.</param>
|
||||
|
/// <returns>The associated vectors ready for packing.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector512<float> ReassociateForStorage(Vector512<float> source) |
||||
|
{ |
||||
|
Vector512<float> zero = Vector512<float>.Zero; |
||||
|
Vector512<float> alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); |
||||
|
Vector512<float> clampedAlpha = ClampUnit(alpha); |
||||
|
Vector512<float> storedAlpha = HalfTypeHelper.RoundToHalf(clampedAlpha); |
||||
|
Vector512<float> result = source * (storedAlpha / alpha); |
||||
|
Vector512<float> alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); |
||||
|
result = Vector512.ConditionalSelect(alphaMask, storedAlpha, result); |
||||
|
result = Vector512.Min(Vector512.Max(result, zero), storedAlpha); |
||||
|
return Vector512.ConditionalSelect(Vector512.LessThanOrEqual(alpha, zero), zero, result); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,227 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Packed pixel type containing four 16-bit floating-point values typically ranging from 0 to 1.
|
||||
|
/// The color components are stored in red, green, blue, and alpha order.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// <see cref="ToVector4"/> and scaled vector conversions return the same component values in the nominal color range
|
||||
|
/// <c>[0, 1]</c>. The packed representation is binary-compatible with <c>DXGI_FORMAT_R16G16B16A16_FLOAT</c>.
|
||||
|
/// </remarks>
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
public partial struct RgbaHalf : IPixel<RgbaHalf>, IPackedVector<ulong> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets or sets the red component.
|
||||
|
/// </summary>
|
||||
|
public Half R; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the green component.
|
||||
|
/// </summary>
|
||||
|
public Half G; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the blue component.
|
||||
|
/// </summary>
|
||||
|
public Half B; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the alpha component.
|
||||
|
/// </summary>
|
||||
|
public Half A; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="RgbaHalf"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="r">The red component.</param>
|
||||
|
/// <param name="g">The green component.</param>
|
||||
|
/// <param name="b">The blue component.</param>
|
||||
|
public RgbaHalf(float r, float g, float b) |
||||
|
: this(r, g, b, 1F) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="RgbaHalf"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="r">The red component.</param>
|
||||
|
/// <param name="g">The green component.</param>
|
||||
|
/// <param name="b">The blue component.</param>
|
||||
|
/// <param name="a">The alpha component.</param>
|
||||
|
public RgbaHalf(float r, float g, float b, float a) |
||||
|
{ |
||||
|
this.R = (Half)r; |
||||
|
this.G = (Half)g; |
||||
|
this.B = (Half)b; |
||||
|
this.A = (Half)a; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="RgbaHalf"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector containing the component values.</param>
|
||||
|
public RgbaHalf(Vector4 vector) |
||||
|
: this(vector.X, vector.Y, vector.Z, vector.W) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public ulong PackedValue |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
readonly get => Unsafe.As<RgbaHalf, ulong>(ref Unsafe.AsRef(in this)); |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
set => Unsafe.As<RgbaHalf, ulong>(ref this) = value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="RgbaHalf"/> values for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">The left value.</param>
|
||||
|
/// <param name="right">The right value.</param>
|
||||
|
/// <returns><see langword="true"/> when the values are equal.</returns>
|
||||
|
public static bool operator ==(RgbaHalf left, RgbaHalf right) => left.Equals(right); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="RgbaHalf"/> values for inequality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">The left value.</param>
|
||||
|
/// <param name="right">The right value.</param>
|
||||
|
/// <returns><see langword="true"/> when the values are not equal.</returns>
|
||||
|
public static bool operator !=(RgbaHalf left, RgbaHalf right) => !left.Equals(right); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Vector4 ToScaledVector4() => this.ToVector4(); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Vector4 ToVector4() => new((float)this.R, (float)this.G, (float)this.B, (float)this.A); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static PixelTypeInfo GetPixelTypeInfo() |
||||
|
=> PixelTypeInfo.Create<RgbaHalf>( |
||||
|
PixelComponentInfo.Create<RgbaHalf>(4, 16, 16, 16, 16), |
||||
|
PixelColorType.RGB | PixelColorType.Alpha, |
||||
|
PixelAlphaRepresentation.Unassociated); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static PixelOperations<RgbaHalf> CreatePixelOperations() => new PixelOperations(); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Vector4 ToAssociatedScaledVector4() |
||||
|
{ |
||||
|
Vector4 vector = this.ToScaledVector4(); |
||||
|
Numerics.Premultiply(ref vector); |
||||
|
return vector; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static RgbaHalf FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static RgbaHalf FromAssociatedScaledVector4(Vector4 source) |
||||
|
{ |
||||
|
Numerics.UnPremultiply(ref source); |
||||
|
return FromScaledVector4(source); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static RgbaHalf FromUnassociatedVector4(Vector4 source) => FromVector4(source); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static RgbaHalf FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static RgbaHalf FromScaledVector4(Vector4 source) => FromVector4(source); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static RgbaHalf FromVector4(Vector4 source) |
||||
|
{ |
||||
|
source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); |
||||
|
return new RgbaHalf(source); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalf FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalf FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalf FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalf FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalf FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalf FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalf FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalf FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalf FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalf FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalf FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalf FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalf FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override readonly bool Equals(object? obj) => obj is RgbaHalf other && this.Equals(other); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public readonly bool Equals(RgbaHalf other) => this.PackedValue.Equals(other.PackedValue); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override readonly string ToString() => FormattableString.Invariant($"RgbaHalf({(float)this.R:#0.##}, {(float)this.G:#0.##}, {(float)this.B:#0.##}, {(float)this.A:#0.##})"); |
||||
|
} |
||||
@ -0,0 +1,242 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Packed pixel type containing four associated 16-bit floating-point values typically ranging from 0 to 1.
|
||||
|
/// The color components are stored in red, green, blue, and alpha order.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// <see cref="ToVector4"/> and scaled vector conversions return the same associated component values in the nominal
|
||||
|
/// color range <c>[0, 1]</c>. The packed representation is binary-compatible with
|
||||
|
/// <c>DXGI_FORMAT_R16G16B16A16_FLOAT</c>.
|
||||
|
/// </remarks>
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
public partial struct RgbaHalfP : IPixel<RgbaHalfP>, IPackedVector<ulong> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets or sets the associated red component.
|
||||
|
/// </summary>
|
||||
|
public Half R; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the associated green component.
|
||||
|
/// </summary>
|
||||
|
public Half G; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the associated blue component.
|
||||
|
/// </summary>
|
||||
|
public Half B; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the alpha component.
|
||||
|
/// </summary>
|
||||
|
public Half A; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="RgbaHalfP"/> struct from associated components.
|
||||
|
/// </summary>
|
||||
|
/// <param name="r">The associated red component.</param>
|
||||
|
/// <param name="g">The associated green component.</param>
|
||||
|
/// <param name="b">The associated blue component.</param>
|
||||
|
public RgbaHalfP(float r, float g, float b) |
||||
|
: this(r, g, b, 1F) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="RgbaHalfP"/> struct from associated components.
|
||||
|
/// </summary>
|
||||
|
/// <param name="r">The associated red component.</param>
|
||||
|
/// <param name="g">The associated green component.</param>
|
||||
|
/// <param name="b">The associated blue component.</param>
|
||||
|
/// <param name="a">The alpha component.</param>
|
||||
|
public RgbaHalfP(float r, float g, float b, float a) |
||||
|
{ |
||||
|
this.R = (Half)r; |
||||
|
this.G = (Half)g; |
||||
|
this.B = (Half)b; |
||||
|
this.A = (Half)a; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="RgbaHalfP"/> struct from an associated vector.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The associated vector.</param>
|
||||
|
public RgbaHalfP(Vector4 vector) |
||||
|
: this() => this = FromAssociatedScaledVector4(vector); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public ulong PackedValue |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
readonly get => Unsafe.As<RgbaHalfP, ulong>(ref Unsafe.AsRef(in this)); |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
set => Unsafe.As<RgbaHalfP, ulong>(ref this) = value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="RgbaHalfP"/> values for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">The left value.</param>
|
||||
|
/// <param name="right">The right value.</param>
|
||||
|
/// <returns><see langword="true"/> when the values are equal.</returns>
|
||||
|
public static bool operator ==(RgbaHalfP left, RgbaHalfP right) => left.Equals(right); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="RgbaHalfP"/> values for inequality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">The left value.</param>
|
||||
|
/// <param name="right">The right value.</param>
|
||||
|
/// <returns><see langword="true"/> when the values are not equal.</returns>
|
||||
|
public static bool operator !=(RgbaHalfP left, RgbaHalfP right) => !left.Equals(right); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToUnassociatedScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Vector4 ToScaledVector4() => this.ToVector4(); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Vector4 ToVector4() => new((float)this.R, (float)this.G, (float)this.B, (float)this.A); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Vector4 ToUnassociatedScaledVector4() |
||||
|
{ |
||||
|
Vector4 vector = this.ToScaledVector4(); |
||||
|
Numerics.UnPremultiply(ref vector); |
||||
|
return vector; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Vector4 ToUnassociatedVector4() => this.ToUnassociatedScaledVector4(); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static PixelTypeInfo GetPixelTypeInfo() |
||||
|
=> PixelTypeInfo.Create<RgbaHalfP>( |
||||
|
PixelComponentInfo.Create<RgbaHalfP>(4, 16, 16, 16, 16), |
||||
|
PixelColorType.RGB | PixelColorType.Alpha, |
||||
|
PixelAlphaRepresentation.Associated); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static PixelOperations<RgbaHalfP> CreatePixelOperations() => new PixelOperations(); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static RgbaHalfP FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static RgbaHalfP FromVector4(Vector4 source) => FromAssociatedVector4(source); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static RgbaHalfP FromUnassociatedScaledVector4(Vector4 source) |
||||
|
{ |
||||
|
source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); |
||||
|
|
||||
|
// RGB must be associated with the alpha value that binary16 storage can reproduce, not the higher-precision input alpha.
|
||||
|
source.W = HalfTypeHelper.Unpack(HalfTypeHelper.Pack(source.W)); |
||||
|
Numerics.Premultiply(ref source); |
||||
|
return new RgbaHalfP(source.X, source.Y, source.Z, source.W); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static RgbaHalfP FromAssociatedScaledVector4(Vector4 source) |
||||
|
{ |
||||
|
float alpha = source.W; |
||||
|
|
||||
|
if (alpha <= 0F) |
||||
|
{ |
||||
|
return default; |
||||
|
} |
||||
|
|
||||
|
float storedAlpha = HalfTypeHelper.Unpack(HalfTypeHelper.Pack(Numerics.Clamp(alpha, 0F, 1F))); |
||||
|
|
||||
|
// Preserve the represented straight color when binary16 rounds alpha, then restore the associated RGB <= alpha invariant.
|
||||
|
source *= storedAlpha / alpha; |
||||
|
source.W = storedAlpha; |
||||
|
Numerics.ClampRgbToAlpha(ref source); |
||||
|
return new RgbaHalfP(source.X, source.Y, source.Z, source.W); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static RgbaHalfP FromUnassociatedVector4(Vector4 source) => FromUnassociatedScaledVector4(source); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static RgbaHalfP FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalfP FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalfP FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalfP FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalfP FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalfP FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalfP FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalfP FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalfP FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalfP FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalfP FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalfP FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalfP FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public static RgbaHalfP FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override readonly bool Equals(object? obj) => obj is RgbaHalfP other && this.Equals(other); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public readonly bool Equals(RgbaHalfP other) => this.PackedValue.Equals(other.PackedValue); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override readonly string ToString() => FormattableString.Invariant($"RgbaHalfP({(float)this.R:#0.##}, {(float)this.G:#0.##}, {(float)this.B:#0.##}, {(float)this.A:#0.##})"); |
||||
|
} |
||||
@ -0,0 +1,283 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
using SixLabors.ImageSharp.Tests.TestUtilities; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.PixelFormats; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Tests the unit-range binary16 RGBA pixel formats.
|
||||
|
/// </summary>
|
||||
|
[Trait("Category", "PixelFormats")] |
||||
|
public class RgbaHalfTests |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Verifies that the unassociated format has the native layout required by RGBA binary16 surfaces.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void RgbaHalfHasRgbaBinary16Layout() |
||||
|
{ |
||||
|
RgbaHalf pixel = new(.25F, .5F, .75F, 1F); |
||||
|
ulong expected = BitConverter.HalfToUInt16Bits((Half).25F) |
||||
|
| ((ulong)BitConverter.HalfToUInt16Bits((Half).5F) << 16) |
||||
|
| ((ulong)BitConverter.HalfToUInt16Bits((Half).75F) << 32) |
||||
|
| ((ulong)BitConverter.HalfToUInt16Bits((Half)1F) << 48); |
||||
|
|
||||
|
Assert.Equal(8, Unsafe.SizeOf<RgbaHalf>()); |
||||
|
Assert.Equal(expected, pixel.PackedValue); |
||||
|
Assert.Equal(new Vector4(.25F, .5F, .75F, 1F), pixel.ToVector4()); |
||||
|
Assert.Equal(pixel.ToVector4(), pixel.ToScaledVector4()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies that zero-filled binary16 storage represents transparent black without affine remapping.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void RgbaHalfDefaultIsTransparentBlack() |
||||
|
{ |
||||
|
RgbaHalf pixel = default; |
||||
|
|
||||
|
Assert.Equal(Vector4.Zero, pixel.ToVector4()); |
||||
|
Assert.Equal(Vector4.Zero, pixel.ToScaledVector4()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies that scaled input is clamped to the pixel format's unit color range.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void RgbaHalfFromScaledVector4ClampsToUnitRange() |
||||
|
{ |
||||
|
RgbaHalf pixel = RgbaHalf.FromScaledVector4(new Vector4(-1F, .5F, 2F, 1F)); |
||||
|
|
||||
|
Assert.Equal(new Vector4(0F, .5F, 1F, 1F), pixel.ToScaledVector4()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies that the associated format stores associated binary16 components in the same RGBA order.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void RgbaHalfPHasAssociatedRgbaBinary16Layout() |
||||
|
{ |
||||
|
RgbaHalfP pixel = new(.125F, .25F, .375F, .5F); |
||||
|
ulong expected = BitConverter.HalfToUInt16Bits((Half).125F) |
||||
|
| ((ulong)BitConverter.HalfToUInt16Bits((Half).25F) << 16) |
||||
|
| ((ulong)BitConverter.HalfToUInt16Bits((Half).375F) << 32) |
||||
|
| ((ulong)BitConverter.HalfToUInt16Bits((Half).5F) << 48); |
||||
|
|
||||
|
Assert.Equal(8, Unsafe.SizeOf<RgbaHalfP>()); |
||||
|
Assert.Equal(expected, pixel.PackedValue); |
||||
|
Assert.Equal(new Vector4(.125F, .25F, .375F, .5F), pixel.ToAssociatedScaledVector4()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies that zero-filled associated binary16 storage represents transparent black.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void RgbaHalfPDefaultIsTransparentBlack() |
||||
|
{ |
||||
|
RgbaHalfP pixel = default; |
||||
|
|
||||
|
Assert.Equal(Vector4.Zero, pixel.ToVector4()); |
||||
|
Assert.Equal(Vector4.Zero, pixel.ToScaledVector4()); |
||||
|
Assert.Equal(Vector4.Zero, pixel.ToUnassociatedScaledVector4()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies that association uses the alpha value that survives binary16 quantization.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void RgbaHalfPQuantizesAlphaBeforeAssociation() |
||||
|
{ |
||||
|
Vector4 source = new(.75F, .5F, .25F, 1F / 3F); |
||||
|
float storedAlpha = (float)(Half)source.W; |
||||
|
RgbaHalfP pixel = RgbaHalfP.FromUnassociatedScaledVector4(source); |
||||
|
|
||||
|
Assert.Equal((Half)(source.X * storedAlpha), pixel.R); |
||||
|
Assert.Equal((Half)(source.Y * storedAlpha), pixel.G); |
||||
|
Assert.Equal((Half)(source.Z * storedAlpha), pixel.B); |
||||
|
Assert.Equal((Half)storedAlpha, pixel.A); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies every bulk representation path against its scalar pixel contract at each SIMD width and tail boundary.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void RgbaHalfBulkConversionsMatchScalarAcrossHardwareWidths() |
||||
|
=> FeatureTestRunner.RunWithHwIntrinsicsFeature( |
||||
|
static () => AssertBulkConversionsMatchScalar<RgbaHalf>(), |
||||
|
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies every associated bulk representation path against its scalar pixel contract at each SIMD width and tail boundary.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void RgbaHalfPBulkConversionsMatchScalarAcrossHardwareWidths() |
||||
|
=> FeatureTestRunner.RunWithHwIntrinsicsFeature( |
||||
|
static () => AssertBulkConversionsMatchScalar<RgbaHalfP>(), |
||||
|
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies the declared component precision and alpha representation for the unassociated format.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void RgbaHalfPixelInformationIsCorrect() => AssertPixelInformation<RgbaHalf>(PixelAlphaRepresentation.Unassociated); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies the declared component precision and alpha representation for the associated format.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void RgbaHalfPPixelInformationIsCorrect() => AssertPixelInformation<RgbaHalfP>(PixelAlphaRepresentation.Associated); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares bulk conversions with the corresponding scalar pixel operations for representative vector widths and remainders.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The binary16 pixel format to test.</typeparam>
|
||||
|
private static void AssertBulkConversionsMatchScalar<TPixel>() |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
{ |
||||
|
int[] lengths = [0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 259]; |
||||
|
PixelConversionModifiers[] modifiers = |
||||
|
[ |
||||
|
PixelConversionModifiers.None, |
||||
|
PixelConversionModifiers.Scale, |
||||
|
PixelConversionModifiers.Premultiply, |
||||
|
PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply, |
||||
|
PixelConversionModifiers.UnPremultiply, |
||||
|
PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply |
||||
|
]; |
||||
|
|
||||
|
foreach (int length in lengths) |
||||
|
{ |
||||
|
TPixel[] pixels = new TPixel[length]; |
||||
|
|
||||
|
for (int i = 0; i < length; i++) |
||||
|
{ |
||||
|
pixels[i] = TPixel.FromUnassociatedScaledVector4(CreateUnassociatedVector(i)); |
||||
|
} |
||||
|
|
||||
|
foreach (PixelConversionModifiers modifier in modifiers) |
||||
|
{ |
||||
|
Vector4[] expectedVectors = new Vector4[length]; |
||||
|
Vector4[] actualVectors = new Vector4[length]; |
||||
|
Vector4[] sourceVectors = new Vector4[length]; |
||||
|
TPixel[] expectedPixels = new TPixel[length]; |
||||
|
TPixel[] actualPixels = new TPixel[length]; |
||||
|
bool associated = RequestsAssociated<TPixel>(modifier); |
||||
|
bool scaled = modifier.IsDefined(PixelConversionModifiers.Scale); |
||||
|
|
||||
|
for (int i = 0; i < length; i++) |
||||
|
{ |
||||
|
expectedVectors[i] = ToScalarVector(pixels[i], associated, scaled); |
||||
|
sourceVectors[i] = CreateUnassociatedVector(i + 19); |
||||
|
|
||||
|
if (associated) |
||||
|
{ |
||||
|
Numerics.Premultiply(ref sourceVectors[i]); |
||||
|
} |
||||
|
|
||||
|
expectedPixels[i] = FromScalarVector<TPixel>(sourceVectors[i], associated, scaled); |
||||
|
} |
||||
|
|
||||
|
PixelOperations<TPixel>.Instance.ToVector4(Configuration.Default, pixels, actualVectors, modifier); |
||||
|
PixelOperations<TPixel>.Instance.FromVector4Destructive(Configuration.Default, sourceVectors.AsSpan(), actualPixels, modifier); |
||||
|
|
||||
|
Assert.Equal(expectedVectors, actualVectors); |
||||
|
Assert.Equal(expectedPixels, actualPixels); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a deterministic unassociated unit-range vector for bulk conversion tests.
|
||||
|
/// </summary>
|
||||
|
/// <param name="index">The source index used to vary the component values.</param>
|
||||
|
/// <returns>The generated vector.</returns>
|
||||
|
private static Vector4 CreateUnassociatedVector(int index) |
||||
|
=> new( |
||||
|
((index * 37) % 101) / 100F, |
||||
|
((index * 53) % 101) / 100F, |
||||
|
((index * 71) % 101) / 100F, |
||||
|
((index * 89) % 101) / 100F); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Resolves whether a modifier combination requests associated output under the pixel format's native representation.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The pixel format whose native representation participates in modifier resolution.</typeparam>
|
||||
|
/// <param name="modifiers">The conversion modifiers.</param>
|
||||
|
/// <returns><see langword="true"/> when the resulting vector representation is associated.</returns>
|
||||
|
private static bool RequestsAssociated<TPixel>(PixelConversionModifiers modifiers) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
=> modifiers.IsDefined(PixelConversionModifiers.Premultiply) |
||||
|
|| (TPixel.GetPixelTypeInfo().AlphaRepresentation == PixelAlphaRepresentation.Associated |
||||
|
&& !modifiers.IsDefined(PixelConversionModifiers.UnPremultiply)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts one pixel through the scalar API selected by the requested representation and range.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The source pixel format.</typeparam>
|
||||
|
/// <param name="pixel">The source pixel.</param>
|
||||
|
/// <param name="associated">Whether the result should use associated alpha.</param>
|
||||
|
/// <param name="scaled">Whether the result should use the scaled range.</param>
|
||||
|
/// <returns>The converted vector.</returns>
|
||||
|
private static Vector4 ToScalarVector<TPixel>(TPixel pixel, bool associated, bool scaled) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
=> (associated, scaled) switch |
||||
|
{ |
||||
|
(true, true) => pixel.ToAssociatedScaledVector4(), |
||||
|
(true, false) => pixel.ToAssociatedVector4(), |
||||
|
(false, true) => pixel.ToUnassociatedScaledVector4(), |
||||
|
_ => pixel.ToUnassociatedVector4() |
||||
|
}; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts one vector through the scalar API selected by its representation and range.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The destination pixel format.</typeparam>
|
||||
|
/// <param name="vector">The source vector.</param>
|
||||
|
/// <param name="associated">Whether the source uses associated alpha.</param>
|
||||
|
/// <param name="scaled">Whether the source uses the scaled range.</param>
|
||||
|
/// <returns>The converted pixel.</returns>
|
||||
|
private static TPixel FromScalarVector<TPixel>(Vector4 vector, bool associated, bool scaled) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
=> (associated, scaled) switch |
||||
|
{ |
||||
|
(true, true) => TPixel.FromAssociatedScaledVector4(vector), |
||||
|
(true, false) => TPixel.FromAssociatedVector4(vector), |
||||
|
(false, true) => TPixel.FromUnassociatedScaledVector4(vector), |
||||
|
_ => TPixel.FromUnassociatedVector4(vector) |
||||
|
}; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies the component layout and alpha metadata exposed by a binary16 pixel format.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The pixel format to inspect.</typeparam>
|
||||
|
/// <param name="alphaRepresentation">The expected alpha representation.</param>
|
||||
|
private static void AssertPixelInformation<TPixel>(PixelAlphaRepresentation alphaRepresentation) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
{ |
||||
|
PixelTypeInfo info = TPixel.GetPixelTypeInfo(); |
||||
|
PixelComponentInfo componentInfo = info.ComponentInfo.Value; |
||||
|
|
||||
|
Assert.Equal(64, info.BitsPerPixel); |
||||
|
Assert.Equal(alphaRepresentation, info.AlphaRepresentation); |
||||
|
Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); |
||||
|
Assert.Equal(4, componentInfo.ComponentCount); |
||||
|
Assert.Equal(0, componentInfo.Padding); |
||||
|
|
||||
|
for (int i = 0; i < componentInfo.ComponentCount; i++) |
||||
|
{ |
||||
|
Assert.Equal(16, componentInfo.GetComponentPrecision(i)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Applies the shared associated-alpha contract to <see cref="RgbaHalfP"/>.
|
||||
|
/// </summary>
|
||||
|
public class RgbaHalfPAssociatedAlphaTests : AssociatedAlphaPixelTests<RgbaHalfP> |
||||
|
{ |
||||
|
} |
||||
Loading…
Reference in new issue