Files
AssetRipper.TextureDecoder/TextureDecoder/Rgb/ConversionUtilities.cs
T
2022-07-10 09:43:35 -04:00

154 lines
4.0 KiB
C#

using System.Runtime.CompilerServices;
namespace AssetRipper.TextureDecoder.Rgb
{
internal static class ConversionUtilities
{
internal static TTo ConvertValue<TFrom, TTo>(TFrom value)
where TFrom : unmanaged
where TTo : unmanaged
{
if (typeof(TFrom) == typeof(byte))
{
return ConvertUInt8<TTo>(Unsafe.As<TFrom, byte>(ref value));
}
else if (typeof(TFrom) == typeof(ushort))
{
return ConvertUInt16<TTo>(Unsafe.As<TFrom, ushort>(ref value));
}
else if (typeof(TFrom) == typeof(Half))
{
return ConvertHalf<TTo>(Unsafe.As<TFrom, Half>(ref value));
}
else if (typeof(TFrom) == typeof(float))
{
return ConvertSingle<TTo>(Unsafe.As<TFrom, float>(ref value));
}
else
{
return default; //exceptions prevent inlining
}
}
private static TTo ConvertUInt8<TTo>(byte value) where TTo : unmanaged
{
if (typeof(TTo) == typeof(byte))
{
return Unsafe.As<byte, TTo>(ref value);
}
else if (typeof(TTo) == typeof(ushort))
{
ushort converted = unchecked((ushort)((uint)value << 8));
return Unsafe.As<ushort, TTo>(ref converted);
}
else if (typeof(TTo) == typeof(Half))
{
Half converted = (Half)(value / (float)byte.MaxValue);
return Unsafe.As<Half, TTo>(ref converted);
}
else if (typeof(TTo) == typeof(float))
{
float converted = (float)(value / (float)byte.MaxValue);
return Unsafe.As<float, TTo>(ref converted);
}
else
{
return default; //exceptions prevent inlining
}
}
private static TTo ConvertUInt16<TTo>(ushort value) where TTo : unmanaged
{
if (typeof(TTo) == typeof(byte))
{
byte converted = unchecked((byte)((uint)value >> 8));
return Unsafe.As<byte, TTo>(ref converted);
}
else if (typeof(TTo) == typeof(ushort))
{
return Unsafe.As<ushort, TTo>(ref value);
}
else if (typeof(TTo) == typeof(Half))
{
Half converted = (Half)(value / (float)ushort.MaxValue);
return Unsafe.As<Half, TTo>(ref converted);
}
else if (typeof(TTo) == typeof(float))
{
float converted = (float)(value / (float)ushort.MaxValue);
return Unsafe.As<float, TTo>(ref converted);
}
else
{
return default; //exceptions prevent inlining
}
}
private static TTo ConvertHalf<TTo>(Half value) where TTo : unmanaged
{
if (typeof(TTo) == typeof(byte))
{
byte converted = ClampByte((float)value * byte.MaxValue);
return Unsafe.As<byte, TTo>(ref converted);
}
else if (typeof(TTo) == typeof(ushort))
{
ushort converted = ClampUInt16((float)value * ushort.MaxValue);
return Unsafe.As<ushort, TTo>(ref converted);
}
else if (typeof(TTo) == typeof(Half))
{
return Unsafe.As<Half, TTo>(ref value);
}
else if (typeof(TTo) == typeof(float))
{
float converted = (float)value;
return Unsafe.As<float, TTo>(ref converted);
}
else
{
return default; //exceptions prevent inlining
}
}
private static TTo ConvertSingle<TTo>(float value) where TTo : unmanaged
{
if (typeof(TTo) == typeof(byte))
{
byte converted = ClampByte(value * byte.MaxValue);
return Unsafe.As<byte, TTo>(ref converted);
}
else if (typeof(TTo) == typeof(ushort))
{
ushort converted = ClampUInt16(value * ushort.MaxValue);
return Unsafe.As<ushort, TTo>(ref converted);
}
else if (typeof(TTo) == typeof(Half))
{
Half converted = (Half)value;
return Unsafe.As<Half, TTo>(ref converted);
}
else if (typeof(TTo) == typeof(float))
{
return Unsafe.As<float, TTo>(ref value);
}
else
{
return default; //exceptions prevent inlining
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte ClampByte(float x)
{
return byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? (byte)x : byte.MinValue);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ushort ClampUInt16(float x)
{
return ushort.MaxValue < x ? ushort.MaxValue : (x > ushort.MinValue ? (ushort)x : ushort.MinValue);
}
}
}