Use System.Half

This commit is contained in:
VaDiM
2025-07-13 20:15:13 +03:00
parent f0029520fb
commit 6f8f1a5a8a
4 changed files with 72 additions and 54 deletions

View File

@ -1413,7 +1413,7 @@ namespace AssetStudio
result[i] = BitConverter.ToSingle(inputBytes, i * 4);
break;
case VertexFormat.Float16:
result[i] = Half.ToHalf(inputBytes, i * 2);
result[i] = (float)HalfHelper.ToHalf(inputBytes, i * 2);
break;
case VertexFormat.UNorm8:
result[i] = inputBytes[i] / 255f;

View File

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Globalization;
#if NETFRAMEWORK
namespace AssetStudio
{
/// <summary>
@ -35,27 +36,27 @@ namespace AssetStudio
/// <summary>
/// Represents the smallest positive System.Half value greater than zero. This field is constant.
/// </summary>
public static readonly Half Epsilon = Half.ToHalf(0x0001);
public static readonly Half Epsilon = HalfHelper.ToHalf(0x0001);
/// <summary>
/// Represents the largest possible value of System.Half. This field is constant.
/// </summary>
public static readonly Half MaxValue = Half.ToHalf(0x7bff);
public static readonly Half MaxValue = HalfHelper.ToHalf(0x7bff);
/// <summary>
/// Represents the smallest possible value of System.Half. This field is constant.
/// </summary>
public static readonly Half MinValue = Half.ToHalf(0xfbff);
public static readonly Half MinValue = HalfHelper.ToHalf(0xfbff);
/// <summary>
/// Represents not a number (NaN). This field is constant.
/// </summary>
public static readonly Half NaN = Half.ToHalf(0xfe00);
public static readonly Half NaN = HalfHelper.ToHalf(0xfe00);
/// <summary>
/// Represents negative infinity. This field is constant.
/// </summary>
public static readonly Half NegativeInfinity = Half.ToHalf(0xfc00);
public static readonly Half NegativeInfinity = HalfHelper.ToHalf(0xfc00);
/// <summary>
/// Represents positive infinity. This field is constant.
/// </summary>
public static readonly Half PositiveInfinity = Half.ToHalf(0x7c00);
public static readonly Half PositiveInfinity = HalfHelper.ToHalf(0x7c00);
#endregion
#region Constructors
@ -63,7 +64,7 @@ namespace AssetStudio
/// Initializes a new instance of System.Half to the value of the specified single-precision floating-point number.
/// </summary>
/// <param name="value">The value to represent as a System.Half.</param>
public Half(float value) { this = HalfHelper.SingleToHalf(value); }
public Half(float value) { this = HalfHelper.ToHalf(value); }
/// <summary>
/// Initializes a new instance of System.Half to the value of the specified 32-bit signed integer.
/// </summary>
@ -314,7 +315,7 @@ namespace AssetStudio
/// </summary>
/// <param name="value">A System.Half to convert.</param>
/// <returns>A single-precision floating-point number that represents the converted System.Half.</returns>
public static implicit operator float(Half value) { return (float)HalfHelper.HalfToSingle(value); }
public static implicit operator float(Half value) { return value.ToSingle(); }
/// <summary>
/// Converts a System.Half to a double-precision floating-point number.
/// </summary>
@ -508,32 +509,6 @@ namespace AssetStudio
{
return value.value;
}
/// <summary>
/// Returns a half-precision floating point number converted from two bytes
/// at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A half-precision floating point number formed by two bytes beginning at startIndex.</returns>
/// <exception cref="System.ArgumentException">
/// startIndex is greater than or equal to the length of value minus 1, and is
/// less than or equal to the length of value minus 1.
/// </exception>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">startIndex is less than zero or greater than the length of value minus 1.</exception>
public static Half ToHalf(byte[] value, int startIndex)
{
return Half.ToHalf((ushort)BitConverter.ToInt16(value, startIndex));
}
/// <summary>
/// Returns a half-precision floating point number converted from its binary representation.
/// </summary>
/// <param name="bits">Binary representation of System.Half value</param>
/// <returns>A half-precision floating point number formed by its binary representation.</returns>
public static Half ToHalf(ushort bits)
{
return new Half { value = bits };
}
/// <summary>
/// Returns a value indicating the sign of a half-precision floating-point number.
@ -886,3 +861,4 @@ namespace AssetStudio
#endregion
}
}
#endif

View File

@ -1,18 +1,32 @@
using System;
using System.Buffers.Binary;
using System.Runtime.InteropServices;
namespace AssetStudio
{
#if NET
public static class HalfHelper
{
public static Half ToHalf(ushort bits)
{
return (Half)bits;
}
public static Half ToHalf(ReadOnlySpan<byte> bytes, int startIndex)
{
return BinaryPrimitives.ReadHalfLittleEndian(bytes[startIndex..]);
}
}
#else
/// <summary>
/// Helper class for Half conversions and some low level operations.
/// This class is internally used in the Half class.
/// </summary>
/// <remarks>
/// References:
/// - Fast Half Float Conversions, Jeroen van der Zijp, link: http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
/// </remarks>
[ComVisible(false)]
internal static class HalfHelper
public static class HalfHelper
{
private static uint[] mantissaTable = GenerateMantissaTable();
private static uint[] exponentTable = GenerateExponentTable();
@ -169,27 +183,54 @@ namespace AssetStudio
ushort result = (ushort)(baseTable[(value >> 23) & 0x1ff] + ((value & 0x007fffff) >> shiftTable[value >> 23]));
return Half.ToHalf(result);
}*/
public static float HalfToSingle(Half half)
/// <summary>
/// Returns a half-precision floating point number converted from two bytes
/// at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A half-precision floating point number formed by two bytes beginning at startIndex.</returns>
/// <exception cref="System.ArgumentException">
/// startIndex is greater than or equal to the length of value minus 1, and is
/// less than or equal to the length of value minus 1.
/// </exception>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">startIndex is less than zero or greater than the length of value minus 1.</exception>
public static Half ToHalf(ReadOnlySpan<byte> value, int startIndex)
{
return ToHalf((ushort)BinaryPrimitives.ReadInt16LittleEndian(value.Slice(startIndex)));
}
/// <summary>
/// Returns a half-precision floating point number converted from its binary representation.
/// </summary>
/// <param name="bits">Binary representation of System.Half value</param>
/// <returns>A half-precision floating point number formed by its binary representation.</returns>
public static Half ToHalf(ushort bits)
{
return new Half { value = bits };
}
public static Half ToHalf(float single)
{
byte[] singleBytes = BitConverter.GetBytes(single);
uint value = BitConverter.ToUInt32(singleBytes, 0);
ushort result = (ushort)(baseTable[(value >> 23) & 0x1ff] + ((value & 0x007fffff) >> shiftTable[value >> 23]));
return ToHalf(result);
}
public static float ToSingle(this Half half)
{
uint result = mantissaTable[offsetTable[half.value >> 10] + (half.value & 0x3ff)] + exponentTable[half.value >> 10];
byte[] uintBytes = BitConverter.GetBytes(result);
return BitConverter.ToSingle(uintBytes, 0);
}
public static Half SingleToHalf(float single)
{
byte[] singleBytes = BitConverter.GetBytes(single);
uint value = BitConverter.ToUInt32(singleBytes, 0);
ushort result = (ushort)(baseTable[(value >> 23) & 0x1ff] + ((value & 0x007fffff) >> shiftTable[value >> 23]));
return Half.ToHalf(result);
}
public static Half Negate(Half half)
{
return Half.ToHalf((ushort)(half.value ^ 0x8000));
return ToHalf((ushort)(half.value ^ 0x8000));
}
public static Half Abs(Half half)
{
return Half.ToHalf((ushort)(half.value & 0x7fff));
return ToHalf((ushort)(half.value & 0x7fff));
}
public static bool IsNaN(Half half)
@ -209,4 +250,5 @@ namespace AssetStudio
return (half.value == 0xfc00);
}
}
#endif
}

View File

@ -440,7 +440,7 @@ namespace AssetStudio
{
buff[i] = 0;
buff[i + 1] = 0;
buff[i + 2] = (byte)MathF.Round(Half.ToHalf(image_data, i / 2) * 255f);
buff[i + 2] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i / 2) * 255f);
buff[i + 3] = 255;
}
return true;
@ -451,8 +451,8 @@ namespace AssetStudio
for (var i = 0; i < outPutDataSize; i += 4)
{
buff[i] = 0;
buff[i + 1] = (byte)MathF.Round(Half.ToHalf(image_data, i + 2) * 255f);
buff[i + 2] = (byte)MathF.Round(Half.ToHalf(image_data, i) * 255f);
buff[i + 1] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i + 2) * 255f);
buff[i + 2] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i) * 255f);
buff[i + 3] = 255;
}
return true;
@ -462,10 +462,10 @@ namespace AssetStudio
{
for (var i = 0; i < outPutDataSize; i += 4)
{
buff[i] = (byte)MathF.Round(Half.ToHalf(image_data, i * 2 + 4) * 255f);
buff[i + 1] = (byte)MathF.Round(Half.ToHalf(image_data, i * 2 + 2) * 255f);
buff[i + 2] = (byte)MathF.Round(Half.ToHalf(image_data, i * 2) * 255f);
buff[i + 3] = (byte)MathF.Round(Half.ToHalf(image_data, i * 2 + 6) * 255f);
buff[i] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i * 2 + 4) * 255f);
buff[i + 1] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i * 2 + 2) * 255f);
buff[i + 2] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i * 2) * 255f);
buff[i + 3] = (byte)MathF.Round((float)HalfHelper.ToHalf(image_data, i * 2 + 6) * 255f);
}
return true;
}