mirror of https://github.com/SixLabors/ImageSharp
Browse Source
This is needed for WriteToOutputStage which uses demote from float to Halfpull/3153/head
2 changed files with 183 additions and 0 deletions
@ -0,0 +1,178 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jxl.Processing.Primitives; |
||||
|
|
||||
|
internal static class JxlHalfUtils |
||||
|
{ |
||||
|
/* |
||||
|
* SIMD version of the following bit-twiddling implementation |
||||
|
* of float to Half conversion, because Vector<T> doesn't |
||||
|
* have a method like this (only reinterpretation, we want |
||||
|
* conversion). |
||||
|
public static ushort ConvertSingleToHalf(float f) |
||||
|
{ |
||||
|
uint x = BitConverter.SingleToUInt32Bits(f); |
||||
|
|
||||
|
uint sign = (x >> 16) & 0x8000; // sign bit
|
||||
|
int exp = (int)((x >> 23) & 0xFF) - 127 + 15; // exponent rebias
|
||||
|
uint mantissa = x & 0x007FFFFF; // mantissa bits
|
||||
|
|
||||
|
if (exp <= 0) |
||||
|
{ |
||||
|
// Subnormal or zero
|
||||
|
if (exp < -10) |
||||
|
{ |
||||
|
return (ushort)sign; |
||||
|
} |
||||
|
|
||||
|
mantissa = (mantissa | 0x00800000) >> (1 - exp); |
||||
|
return (ushort)(sign | (mantissa >> 13)); |
||||
|
} |
||||
|
else if (exp >= 31) |
||||
|
{ |
||||
|
// Inf or NaN
|
||||
|
return (ushort)(sign | 0x7C00 | (mantissa >> 13)); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// Normalized
|
||||
|
return (ushort)(sign | ((uint)exp << 10) | (mantissa >> 13)); |
||||
|
} |
||||
|
} |
||||
|
*/ |
||||
|
public static Vector<ushort> ConvertSingleToHalf(Vector<float> f) |
||||
|
{ |
||||
|
Vector<uint> x = f.As<float, uint>(); |
||||
|
|
||||
|
Vector<uint> sign = (x >> 16) & Vector.Create(0x8000u); |
||||
|
Vector<int> exponent = ((x >> 23) & Vector.Create(0xFFu)).As<uint, int>() - Vector.Create(127) + Vector.Create(15); |
||||
|
Vector<uint> mantissa = x & Vector.Create(0x007FFFFFu); |
||||
|
|
||||
|
Vector<uint> results = Vector<uint>.Zero; |
||||
|
|
||||
|
Vector<uint> lessThanMinus10 = Vector.LessThan(exponent, Vector.Create(-10)).As<int, uint>(); |
||||
|
Vector<uint> gte31 = Vector.GreaterThanOrEqual(exponent, Vector.Create(31)).As<int, uint>(); |
||||
|
Vector<uint> notLtMinus10OrGte31 = ~(lessThanMinus10 | gte31); |
||||
|
|
||||
|
// Subnormal or zero (exp < -10) = sign
|
||||
|
results = Vector.ConditionalSelect(lessThanMinus10, sign, results); |
||||
|
|
||||
|
// exp <= 0 = (ushort)(sign | (((mantissa | 0x00800000) >> (1 - exp)) >> 13));
|
||||
|
results = Vector.ConditionalSelect( |
||||
|
Vector.LessThanOrEqual(exponent, Vector<int>.Zero).As<int, uint>(), |
||||
|
sign | (ShiftRightAll(mantissa.As<uint, int>() | Vector.Create(0x00800000), Vector<int>.One - exponent) >> 13).As<int, uint>(), |
||||
|
results); |
||||
|
|
||||
|
// exp >= 31 = (ushort)(sign | 0x7C00 | (mantissa >> 13))
|
||||
|
results = Vector.ConditionalSelect( |
||||
|
gte31, |
||||
|
sign | Vector.Create(0x7C00u) | (mantissa >> 13), |
||||
|
results); |
||||
|
|
||||
|
// anything else - normalized
|
||||
|
results = Vector.ConditionalSelect( |
||||
|
notLtMinus10OrGte31, |
||||
|
sign | (exponent.As<int, uint>() << 10) | (mantissa >> 13), |
||||
|
results); |
||||
|
|
||||
|
return VectorUInt32ToUInt16(results); |
||||
|
} |
||||
|
|
||||
|
private static Vector<ushort> VectorUInt32ToUInt16(Vector<uint> uint32) |
||||
|
{ |
||||
|
Vector<uint> clipped = uint32 & Vector.Create(0xFFFFu); |
||||
|
return clipped.As<uint, ushort>(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Vectors don't support shifting right using shift value as vector.
|
||||
|
/// This is a hack to do this.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">Kind of vector</typeparam>
|
||||
|
/// <param name="vec">Input vector</param>
|
||||
|
/// <param name="shiftVec">Vectors with shift value for each corresponding item.</param>
|
||||
|
/// <returns>Shifted vectors</returns>
|
||||
|
private static Vector<T> ShiftRightAll<T>(Vector<T> vec, Vector<T> shiftVec) |
||||
|
where T : unmanaged, IShiftOperators<T, T, T> |
||||
|
{ |
||||
|
Span<T> firstVec = stackalloc T[Vector<T>.Count]; |
||||
|
Span<T> secondVec = stackalloc T[Vector<T>.Count]; |
||||
|
|
||||
|
vec.CopyTo(firstVec); |
||||
|
shiftVec.CopyTo(secondVec); |
||||
|
|
||||
|
ref T firstRef = ref MemoryMarshal.GetReference(firstVec); |
||||
|
ref T secondRef = ref MemoryMarshal.GetReference(secondVec); |
||||
|
|
||||
|
int count = Vector<T>.Count; |
||||
|
|
||||
|
if (count == 1) |
||||
|
{ |
||||
|
return Vector.Create(firstRef >>> secondRef); |
||||
|
} |
||||
|
else if (count == 2) |
||||
|
{ |
||||
|
firstRef >>>= secondRef; |
||||
|
Unsafe.Add(ref firstRef, 1) >>>= Unsafe.Add(ref secondRef, 1); |
||||
|
return Vector.Create<T>(firstVec); |
||||
|
} |
||||
|
else if (count == 4) |
||||
|
{ |
||||
|
firstRef >>>= secondRef; |
||||
|
Unsafe.Add(ref firstRef, 1) >>>= Unsafe.Add(ref secondRef, 1); |
||||
|
Unsafe.Add(ref firstRef, 2) >>>= Unsafe.Add(ref secondRef, 2); |
||||
|
Unsafe.Add(ref firstRef, 3) >>>= Unsafe.Add(ref secondRef, 3); |
||||
|
return Vector.Create<T>(firstVec); |
||||
|
} |
||||
|
else if (count == 8) |
||||
|
{ |
||||
|
firstRef >>>= secondRef; |
||||
|
Unsafe.Add(ref firstRef, 1) >>>= Unsafe.Add(ref secondRef, 1); |
||||
|
Unsafe.Add(ref firstRef, 2) >>>= Unsafe.Add(ref secondRef, 2); |
||||
|
Unsafe.Add(ref firstRef, 3) >>>= Unsafe.Add(ref secondRef, 3); |
||||
|
Unsafe.Add(ref firstRef, 4) >>>= Unsafe.Add(ref secondRef, 4); |
||||
|
Unsafe.Add(ref firstRef, 5) >>>= Unsafe.Add(ref secondRef, 5); |
||||
|
Unsafe.Add(ref firstRef, 6) >>>= Unsafe.Add(ref secondRef, 6); |
||||
|
Unsafe.Add(ref firstRef, 7) >>>= Unsafe.Add(ref secondRef, 7); |
||||
|
return Vector.Create<T>(firstVec); |
||||
|
} |
||||
|
else if (count == 16) |
||||
|
{ |
||||
|
firstRef >>>= secondRef; |
||||
|
Unsafe.Add(ref firstRef, 1) >>>= Unsafe.Add(ref secondRef, 1); |
||||
|
Unsafe.Add(ref firstRef, 2) >>>= Unsafe.Add(ref secondRef, 2); |
||||
|
Unsafe.Add(ref firstRef, 3) >>>= Unsafe.Add(ref secondRef, 3); |
||||
|
Unsafe.Add(ref firstRef, 4) >>>= Unsafe.Add(ref secondRef, 4); |
||||
|
Unsafe.Add(ref firstRef, 5) >>>= Unsafe.Add(ref secondRef, 5); |
||||
|
Unsafe.Add(ref firstRef, 6) >>>= Unsafe.Add(ref secondRef, 6); |
||||
|
Unsafe.Add(ref firstRef, 7) >>>= Unsafe.Add(ref secondRef, 7); |
||||
|
Unsafe.Add(ref firstRef, 8) >>>= Unsafe.Add(ref secondRef, 8); |
||||
|
Unsafe.Add(ref firstRef, 9) >>>= Unsafe.Add(ref secondRef, 9); |
||||
|
Unsafe.Add(ref firstRef, 10) >>>= Unsafe.Add(ref secondRef, 10); |
||||
|
Unsafe.Add(ref firstRef, 11) >>>= Unsafe.Add(ref secondRef, 11); |
||||
|
Unsafe.Add(ref firstRef, 12) >>>= Unsafe.Add(ref secondRef, 12); |
||||
|
Unsafe.Add(ref firstRef, 13) >>>= Unsafe.Add(ref secondRef, 13); |
||||
|
Unsafe.Add(ref firstRef, 14) >>>= Unsafe.Add(ref secondRef, 14); |
||||
|
Unsafe.Add(ref firstRef, 15) >>>= Unsafe.Add(ref secondRef, 15); |
||||
|
return Vector.Create<T>(firstVec); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// Vector too large, use a (slightly) slower loop
|
||||
|
// instead of duplicating too much.
|
||||
|
firstRef >>>= secondRef; |
||||
|
|
||||
|
for (int i = 1; i < count; i++) |
||||
|
{ |
||||
|
Unsafe.Add(ref firstRef, i) >>>= Unsafe.Add(ref secondRef, i); |
||||
|
} |
||||
|
|
||||
|
return Vector.Create<T>(firstVec); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue