mirror of
https://github.com/AssetRipper/AssetRipper.TextureDecoder.git
synced 2026-11-13 04:26:30 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13b4f6ee49 | ||
|
|
2cdd532c31 | ||
|
|
d772c2790d | ||
|
|
0201123b0a | ||
|
|
02531ab941 | ||
|
|
2f8d0b4a8b | ||
|
|
0fb5e24d81 | ||
|
|
ec91662b4a | ||
|
|
03713d8d57 | ||
|
|
3cf2170242 | ||
|
|
0a8bae09cf | ||
|
|
a7a12fdd72 | ||
|
|
801edea4cf | ||
|
|
3cb859c6ac | ||
|
|
0738ba4b50 | ||
|
|
09067cecbb | ||
|
|
ec6754a5d2 | ||
|
|
bc4d0f5378 | ||
|
|
8f0f94695b | ||
|
|
aacbfbd222 | ||
|
|
93fe6948b6 | ||
|
|
0e6f3080be | ||
|
|
f8294eff8b | ||
|
|
0b4d1dfc83 | ||
|
|
f5c1aca0e1 | ||
|
|
d4d335d94b | ||
|
|
70775daca9 | ||
|
|
3a94ea1a0f | ||
|
|
db875125d2 |
No files matched your search
@@ -12,9 +12,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v7
|
||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v5.0.0
|
uses: actions/setup-dotnet@v6.0.0
|
||||||
with:
|
with:
|
||||||
dotnet-version: 9.0.x
|
dotnet-version: 9.0.x
|
||||||
- name: Restore dependencies
|
- name: Restore dependencies
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
using AssetRipper.TextureDecoder.Rgb;
|
|
||||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
|
||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
|
|
||||||
namespace AssetRipper.TextureDecoder.Benchmarks;
|
|
||||||
|
|
||||||
public class Argb32Decoding
|
|
||||||
{
|
|
||||||
private const int Width = 256;//ignoring smaller mips
|
|
||||||
private const int Height = 256;//ignoring smaller mips
|
|
||||||
private readonly byte[] inputData;
|
|
||||||
private readonly byte[] outputData;
|
|
||||||
|
|
||||||
public Argb32Decoding()
|
|
||||||
{
|
|
||||||
inputData = File.ReadAllBytes(PathConstants.PathToTestFiles + "Rgb/" + "test.argb32");
|
|
||||||
outputData = new byte[Width * Height * sizeof(uint)];
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public int OriginalMethod() => RgbConverter.ARGB32ToBGRA32(inputData, Width, Height, outputData);
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public int GenericMethod() => RgbConverter.Convert<ColorARGB<byte>, byte, ColorBGRA<byte>, byte>(inputData, Width, Height, outputData);
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.15.4" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\AssetRipper.TextureDecoder\AssetRipper.TextureDecoder.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace AssetRipper.TextureDecoder.Benchmarks;
|
|
||||||
|
|
||||||
internal static class PathConstants
|
|
||||||
{
|
|
||||||
internal const string PathToRepositoryRoot = "../../../../../../../../";
|
|
||||||
internal const string PathToTestFiles = PathToRepositoryRoot + "TestFiles/";
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using BenchmarkDotNet.Running;
|
|
||||||
|
|
||||||
namespace AssetRipper.TextureDecoder.Benchmarks;
|
|
||||||
|
|
||||||
internal class Program
|
|
||||||
{
|
|
||||||
static void Main()
|
|
||||||
{
|
|
||||||
BenchmarkRunner.Run(typeof(Program).Assembly);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using AssetRipper.TextureDecoder.Rgb;
|
|
||||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
|
||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
|
|
||||||
namespace AssetRipper.TextureDecoder.Benchmarks;
|
|
||||||
|
|
||||||
public class Rgb9e5Decoding
|
|
||||||
{
|
|
||||||
private const int Width = 256;//ignoring smaller mips
|
|
||||||
private const int Height = 256;//ignoring smaller mips
|
|
||||||
private readonly byte[] inputData;
|
|
||||||
private readonly byte[] outputData;
|
|
||||||
|
|
||||||
public Rgb9e5Decoding()
|
|
||||||
{
|
|
||||||
inputData = File.ReadAllBytes(PathConstants.PathToTestFiles + "Rgb/" + "test.rgb9e5float");
|
|
||||||
outputData = new byte[Width * Height * sizeof(uint)];
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public int OriginalMethod() => RgbConverter.RGB9e5FloatToBGRA32(inputData, Width, Height, outputData);
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public int GenericMethod() => RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA<byte>, byte>(inputData, Width, Height, outputData);
|
|
||||||
}
|
|
||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.2.2" />
|
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.2.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -82,14 +82,14 @@ internal static class NumericConversionGenerator
|
|||||||
writer.WriteLine("if (typeof(TFrom) == typeof(TTo))");
|
writer.WriteLine("if (typeof(TFrom) == typeof(TTo))");
|
||||||
using (new CurlyBrackets(writer))
|
using (new CurlyBrackets(writer))
|
||||||
{
|
{
|
||||||
writer.WriteLine("return Unsafe.As<TFrom, TTo>(ref value);");
|
writer.WriteLine("return Unsafe.BitCast<TFrom, TTo>(value);");
|
||||||
}
|
}
|
||||||
foreach (CSharpPrimitives.Data from in CSharpPrimitives.List)
|
foreach (CSharpPrimitives.Data from in CSharpPrimitives.List)
|
||||||
{
|
{
|
||||||
writer.WriteLine($"else if (typeof(TFrom) == typeof({from.LangName}))");
|
writer.WriteLine($"else if (typeof(TFrom) == typeof({from.LangName}))");
|
||||||
using (new CurlyBrackets(writer))
|
using (new CurlyBrackets(writer))
|
||||||
{
|
{
|
||||||
writer.WriteLine($"return {ConvertMethodName(from.Type)}<TTo>(Unsafe.As<TFrom, {from.LangName}>(ref value));");
|
writer.WriteLine($"return {ConvertMethodName(from.Type)}<TTo>(Unsafe.BitCast<TFrom, {from.LangName}>(value));");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writer.WriteLine("else");
|
writer.WriteLine("else");
|
||||||
@@ -133,40 +133,41 @@ internal static class NumericConversionGenerator
|
|||||||
{
|
{
|
||||||
if (from == to)
|
if (from == to)
|
||||||
{
|
{
|
||||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref value);");
|
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(value);");
|
||||||
}
|
}
|
||||||
else if (to.HasPointerSize(out CSharpPrimitives.Data? bit32To, out CSharpPrimitives.Data? bit64To))
|
else if (to.HasPointerSize(out CSharpPrimitives.Data? bit32To, out CSharpPrimitives.Data? bit64To))
|
||||||
{
|
{
|
||||||
using (new If(writer, "IntPtr.Size == sizeof(int)"))
|
using (new If(writer, "IntPtr.Size == sizeof(int)"))
|
||||||
{
|
{
|
||||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName}){methodName}<{bit32To.LangName}>(value);");
|
writer.WriteLine($"{to.LangName} converted = ({to.LangName}){methodName}<{bit32To.LangName}>(value);");
|
||||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||||
}
|
}
|
||||||
using (new Else(writer))
|
using (new Else(writer))
|
||||||
{
|
{
|
||||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName}){methodName}<{bit64To.LangName}>(value);");
|
writer.WriteLine($"{to.LangName} converted = ({to.LangName}){methodName}<{bit64To.LangName}>(value);");
|
||||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (to.IsSignedInteger(out CSharpPrimitives.Data? unsignedTo))
|
else if (to.IsSignedInteger(out CSharpPrimitives.Data? unsignedTo))
|
||||||
{
|
{
|
||||||
writer.WriteLine($"{to.LangName} converted = {ChangeSign}({methodName}<{unsignedTo.LangName}>(value));");
|
writer.WriteLine($"{to.LangName} converted = {ChangeSign}({methodName}<{unsignedTo.LangName}>(value));");
|
||||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||||
}
|
}
|
||||||
else if (from.IsFloatingPoint)
|
else if (from.IsFloatingPoint)
|
||||||
{
|
{
|
||||||
if (to.IsFloatingPoint)
|
if (to.IsFloatingPoint)
|
||||||
{
|
{
|
||||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName})value;");
|
writer.WriteLine($"{to.LangName} converted = ({to.LangName})value;");
|
||||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.Assert(to.IsUnsignedInteger);
|
Debug.Assert(to.IsUnsignedInteger);
|
||||||
if (from.Type == typeof(Half))
|
CSharpPrimitives.Data conversionType = CSharpPrimitives.GetDataForIntegerFloatingPointConversion(to, from);
|
||||||
|
if (conversionType != from)
|
||||||
{
|
{
|
||||||
writer.WriteComment("We use float because it has enough precision to convert from Half to any integer type.");
|
writer.WriteComment($"We use {conversionType.LangName} because it has enough precision to convert from {from.LangName} to {to.LangName}.");
|
||||||
writer.WriteLine($"return {ConvertMethodName(typeof(float))}<TTo>((float)value);");
|
writer.WriteLine($"return {ConvertMethodName(conversionType.Type)}<TTo>(({conversionType.LangName})value);");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -175,7 +176,7 @@ internal static class NumericConversionGenerator
|
|||||||
string maxValue = to.MaxValue;
|
string maxValue = to.MaxValue;
|
||||||
writer.WriteLine($"{from.LangName} x = value * ({from.LangName}){maxValue};");
|
writer.WriteLine($"{from.LangName} x = value * ({from.LangName}){maxValue};");
|
||||||
writer.WriteLine($"{to.LangName} converted = ({from.LangName}){maxValue} < x ? {maxValue} : (x > ({from.LangName}){minValue} ? ({to.LangName})x : {minValue});");
|
writer.WriteLine($"{to.LangName} converted = ({from.LangName}){maxValue} < x ? {maxValue} : (x > ({from.LangName}){minValue} ? ({to.LangName})x : {minValue});");
|
||||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,17 +185,18 @@ internal static class NumericConversionGenerator
|
|||||||
Debug.Assert(from.IsUnsignedInteger);
|
Debug.Assert(from.IsUnsignedInteger);
|
||||||
if (to.IsFloatingPoint)
|
if (to.IsFloatingPoint)
|
||||||
{
|
{
|
||||||
if (to.Type == typeof(Half))
|
CSharpPrimitives.Data conversionType = CSharpPrimitives.GetDataForIntegerFloatingPointConversion(from, to);
|
||||||
|
if (conversionType != to)
|
||||||
{
|
{
|
||||||
writer.WriteComment("There isn't enough precision to convert from anything bigger than byte to Half, so we convert to float first.");
|
writer.WriteComment($"There isn't enough precision to convert from {from.LangName} to {to.LangName}, so we convert to {conversionType.LangName} first.");
|
||||||
writer.WriteLine($"float x = {methodName}<float>(value);");
|
writer.WriteLine($"{conversionType.LangName} x = {methodName}<{conversionType.LangName}>(value);");
|
||||||
writer.WriteLine("Half converted = (Half)x;");
|
writer.WriteLine($"{to.LangName} converted = ({to.LangName})x;");
|
||||||
writer.WriteLine("return Unsafe.As<Half, TTo>(ref converted);");
|
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName})value / ({to.LangName}){from.MaxValue};");
|
writer.WriteLine($"{to.LangName} converted = ({to.LangName})value / ({to.LangName}){from.MaxValue};");
|
||||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -214,7 +216,7 @@ internal static class NumericConversionGenerator
|
|||||||
writer.Write($"(({conversionType})value << {i * from.Size * 8}) | ");
|
writer.Write($"(({conversionType})value << {i * from.Size * 8}) | ");
|
||||||
}
|
}
|
||||||
writer.WriteLine("value);");
|
writer.WriteLine("value);");
|
||||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -225,7 +227,14 @@ internal static class NumericConversionGenerator
|
|||||||
writer.WriteComment("This is a special case where we already know an optimal algorithm.");
|
writer.WriteComment("This is a special case where we already know an optimal algorithm.");
|
||||||
writer.WriteLine("uint x = (value * 255u + 32895u) >> 16;");
|
writer.WriteLine("uint x = (value * 255u + 32895u) >> 16;");
|
||||||
writer.WriteLine("byte converted = unchecked((byte)x);");
|
writer.WriteLine("byte converted = unchecked((byte)x);");
|
||||||
writer.WriteLine("return Unsafe.As<byte, TTo>(ref converted);");
|
writer.WriteLine("return Unsafe.BitCast<byte, TTo>(converted);");
|
||||||
|
}
|
||||||
|
else if (from.Size < sizeof(double) && to.Size < sizeof(double))
|
||||||
|
{
|
||||||
|
writer.WriteLine($"double interpolated = (double)value / (double){from.MaxValue};");
|
||||||
|
writer.WriteLine($"double exact = interpolated * (double){to.MaxValue};");
|
||||||
|
writer.WriteLine($"{to.LangName} converted = ({to.LangName})double.Round(exact, MidpointRounding.AwayFromZero);");
|
||||||
|
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -236,7 +245,7 @@ internal static class NumericConversionGenerator
|
|||||||
{
|
{
|
||||||
int offset = (from.Size / to.Size - 1) * to.Size * 8;
|
int offset = (from.Size / to.Size - 1) * to.Size * 8;
|
||||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName})(({conversionType})value >> {offset});");
|
writer.WriteLine($"{to.LangName} converted = ({to.LangName})(({conversionType})value >> {offset});");
|
||||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,7 +276,7 @@ internal static class NumericConversionGenerator
|
|||||||
using (new CurlyBrackets(writer))
|
using (new CurlyBrackets(writer))
|
||||||
{
|
{
|
||||||
writer.WriteLine($"{from.LangName} value = {methodName}Safe<{from.LangName}>();");
|
writer.WriteLine($"{from.LangName} value = {methodName}Safe<{from.LangName}>();");
|
||||||
writer.WriteLine($"return Unsafe.As<{from.LangName}, T>(ref value);");
|
writer.WriteLine($"return Unsafe.BitCast<{from.LangName}, T>(value);");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writer.WriteLine("else");
|
writer.WriteLine("else");
|
||||||
|
|||||||
@@ -13,18 +13,23 @@ internal static partial class Program
|
|||||||
private const string FormatsNamespace = "AssetRipper.TextureDecoder.Rgb.Formats";
|
private const string FormatsNamespace = "AssetRipper.TextureDecoder.Rgb.Formats";
|
||||||
private const string FormatsFolder = "../../../../AssetRipper.TextureDecoder/Rgb/Formats/";
|
private const string FormatsFolder = "../../../../AssetRipper.TextureDecoder/Rgb/Formats/";
|
||||||
|
|
||||||
/// <summary>
|
private readonly record struct CustomColorDetails(string Name, Type Type, bool HasRed, bool HasGreen, bool HasBlue, bool HasAlpha, bool FullyUtilized)
|
||||||
/// Name, Type, Red, Blue, Green, Alpha, Fully Utilized
|
|
||||||
/// </summary>
|
|
||||||
private static readonly List<(string, Type, bool, bool, bool, bool, bool)> CustomColors = new()
|
|
||||||
{
|
{
|
||||||
|
public static implicit operator CustomColorDetails((string, Type, bool, bool, bool, bool, bool) details)
|
||||||
|
{
|
||||||
|
return new CustomColorDetails(details.Item1, details.Item2, details.Item3, details.Item4, details.Item5, details.Item6, details.Item7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly CustomColorDetails[] CustomColors =
|
||||||
|
[
|
||||||
( "ColorARGB16", typeof(byte), true, true, true, true, false ),
|
( "ColorARGB16", typeof(byte), true, true, true, true, false ),
|
||||||
( "ColorRGB16", typeof(byte), true, true, true, false, false ),
|
( "ColorRGB16", typeof(byte), true, true, true, false, false ),
|
||||||
( "ColorRGB9e5", typeof(double), true, true, true, false, false ),
|
( "ColorRGB9e5", typeof(double), true, true, true, false, false ),
|
||||||
( "ColorRGBA16", typeof(byte), true, true, true, true, false ),
|
( "ColorRGBA16", typeof(byte), true, true, true, true, false ),
|
||||||
};
|
];
|
||||||
|
|
||||||
private static readonly List<string> GenericColors =
|
private static readonly string[] GenericColors =
|
||||||
[
|
[
|
||||||
"ColorR",
|
"ColorR",
|
||||||
"ColorRG",
|
"ColorRG",
|
||||||
@@ -38,7 +43,7 @@ internal static partial class Program
|
|||||||
|
|
||||||
static void Main()
|
static void Main()
|
||||||
{
|
{
|
||||||
foreach (var customColor in CustomColors)
|
foreach (CustomColorDetails customColor in CustomColors)
|
||||||
{
|
{
|
||||||
WriteCustomColor(customColor);
|
WriteCustomColor(customColor);
|
||||||
}
|
}
|
||||||
@@ -54,7 +59,7 @@ internal static partial class Program
|
|||||||
Console.WriteLine("Done!");
|
Console.WriteLine("Done!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteCustomColor((string, Type, bool, bool, bool, bool, bool) details)
|
private static void WriteCustomColor(CustomColorDetails details)
|
||||||
{
|
{
|
||||||
(string name, Type type, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized) = details;
|
(string name, Type type, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized) = details;
|
||||||
Console.WriteLine(name);
|
Console.WriteLine(name);
|
||||||
|
|||||||
@@ -68,11 +68,18 @@ public static class CSharpPrimitives
|
|||||||
return type == typeof(Half) || type == typeof(float) || type == typeof(NFloat) || type == typeof(double) || type == typeof(decimal);
|
return type == typeof(Half) || type == typeof(float) || type == typeof(NFloat) || type == typeof(double) || type == typeof(decimal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsInteger(Type type)
|
||||||
|
{
|
||||||
|
return IsUnsignedInteger(type) || IsSignedInteger(type);
|
||||||
|
}
|
||||||
|
|
||||||
public static bool IsUnsignedInteger(Type type)
|
public static bool IsUnsignedInteger(Type type)
|
||||||
{
|
{
|
||||||
return type == typeof(byte) || type == typeof(ushort) || type == typeof(uint) || type == typeof(nuint) || type == typeof(ulong) || type == typeof(UInt128);
|
return type == typeof(byte) || type == typeof(ushort) || type == typeof(uint) || type == typeof(nuint) || type == typeof(ulong) || type == typeof(UInt128);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsSignedInteger(Type type) => IsSignedInteger(type, out _);
|
||||||
|
|
||||||
public static bool IsSignedInteger(Type type, [NotNullWhen(true)] out Type? unsignedType)
|
public static bool IsSignedInteger(Type type, [NotNullWhen(true)] out Type? unsignedType)
|
||||||
{
|
{
|
||||||
if (type == typeof(sbyte))
|
if (type == typeof(sbyte))
|
||||||
@@ -195,6 +202,21 @@ public static class CSharpPrimitives
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Data GetDataForIntegerFloatingPointConversion(Data integerType, Data floatingPointType)
|
||||||
|
{
|
||||||
|
if (!IsInteger(integerType.Type))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The provided integer type is not an integer type.", nameof(integerType));
|
||||||
|
}
|
||||||
|
if (!IsFloatingPoint(floatingPointType.Type))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The provided floating point type is not a floating point type.", nameof(floatingPointType));
|
||||||
|
}
|
||||||
|
|
||||||
|
Data result = integerType.Size < sizeof(uint) ? Dictionary[typeof(float)] : Dictionary[typeof(double)];
|
||||||
|
return floatingPointType.Size >= result.Size ? floatingPointType : result;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract class Data
|
public abstract class Data
|
||||||
{
|
{
|
||||||
public abstract Type Type { get; }
|
public abstract Type Type { get; }
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.2.2" />
|
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.2.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -5,17 +5,13 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
|
||||||
<PackageReference Include="NUnit" Version="4.4.0" />
|
<PackageReference Include="NUnit" Version="4.6.1" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="5.1.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
|
||||||
<PackageReference Include="NUnit.Analyzers" Version="4.10.0">
|
<PackageReference Include="NUnit.Analyzers" Version="4.14.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.4">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ public sealed class AstcTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(new byte[16] { 83, 1, 147, 104, 198, 173, 55, 116, 182, 66, 105, 11, 102, 43, 148, 125 }, 8, 8, TestName = "GetBits Should not throw in DecodeIntseq")]
|
[TestCase(new byte[16] { 83, 1, 147, 104, 198, 173, 55, 116, 182, 66, 105, 11, 102, 43, 148, 125 }, 8, 8, TestName = "GetBits Should not throw in DecodeIntseq")]
|
||||||
|
[TestCase(new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 8, 8, TestName = "Zero blocks should not throw", Description = "Blocks of all zeros are valid")]
|
||||||
|
[TestCase(new byte[16] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 8, 8, TestName = "0xFF blocks should not throw", Description = "Blocks with all bits set are invalid, but should be handled gracefully")]
|
||||||
public void DecodesBlockWithoutThrowing(byte[] block, int blockWidth, int blockHeight)
|
public void DecodesBlockWithoutThrowing(byte[] block, int blockWidth, int blockHeight)
|
||||||
{
|
{
|
||||||
Assert.DoesNotThrow(() => AstcDecoder.DecodeASTC<ColorBGRA<byte>, byte>(block, blockWidth, blockHeight, blockWidth, blockHeight, out _));
|
Assert.DoesNotThrow(() => AstcDecoder.DecodeASTC<ColorBGRA<byte>, byte>(block, blockWidth, blockHeight, blockWidth, blockHeight, out _));
|
||||||
|
|||||||
@@ -0,0 +1,512 @@
|
|||||||
|
using System.Buffers.Binary;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace AssetRipper.TextureDecoder.Tests;
|
||||||
|
|
||||||
|
internal static class OldRgbConverter
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int A8ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return A8ToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int A8ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
output[oo + 0] = 0; // b
|
||||||
|
output[oo + 1] = 0; // g
|
||||||
|
output[oo + 2] = 0; // r
|
||||||
|
output[oo + 3] = input[io]; // a
|
||||||
|
oo += 4;
|
||||||
|
io++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGB24ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RGB24ToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGB24ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
output[oo + 0] = input[io + 2]; // b
|
||||||
|
output[oo + 1] = input[io + 1]; // g
|
||||||
|
output[oo + 2] = input[io + 0]; // r
|
||||||
|
output[oo + 3] = 255; // a
|
||||||
|
io += 3;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGBA32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RGBA32ToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGBA32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
output[oo + 0] = input[io + 2]; // b
|
||||||
|
output[oo + 1] = input[io + 1]; // g
|
||||||
|
output[oo + 2] = input[io + 0]; // r
|
||||||
|
output[oo + 3] = input[io + 3]; // a
|
||||||
|
io += 4;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int ARGB32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return ARGB32ToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int ARGB32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
output[oo + 0] = input[io + 3]; // b
|
||||||
|
output[oo + 1] = input[io + 2]; // g
|
||||||
|
output[oo + 2] = input[io + 1]; // r
|
||||||
|
output[oo + 3] = input[io + 0]; // a
|
||||||
|
io += 4;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGB16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RGB16ToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGB16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
byte r = (byte)(input[io + 1] & 0xF8);
|
||||||
|
byte g = unchecked((byte)((input[io + 1] << 5) | ((input[io + 0] & 0xE0) >> 3)));
|
||||||
|
byte b = unchecked((byte)(input[io + 0] << 3));
|
||||||
|
output[oo + 0] = b; // b
|
||||||
|
output[oo + 1] = g; // g
|
||||||
|
output[oo + 2] = r; // r
|
||||||
|
output[oo + 3] = 255; // a
|
||||||
|
io += 2;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RG16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RG16ToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RG16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
output[oo + 0] = 0; // b
|
||||||
|
output[oo + 1] = input[io + 1]; // g
|
||||||
|
output[oo + 2] = input[io + 0]; // r
|
||||||
|
output[oo + 3] = 255; // a
|
||||||
|
io += 2;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int R8ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return R8ToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int R8ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
output[oo + 0] = 0; // b
|
||||||
|
output[oo + 1] = 0; // g
|
||||||
|
output[oo + 2] = input[io + 0]; // r
|
||||||
|
output[oo + 3] = 255; // a
|
||||||
|
io += 1;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RHalfToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
byte r = ClampByte(ToHalf(input, io) * 255f);
|
||||||
|
output[oo + 0] = 0; // b
|
||||||
|
output[oo + 1] = 0; // g
|
||||||
|
output[oo + 2] = r; // r
|
||||||
|
output[oo + 3] = 255; // a
|
||||||
|
io += 2;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RGHalfToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
byte r = ClampByte(ToHalf(input, io + 0) * 255f);
|
||||||
|
byte g = ClampByte(ToHalf(input, io + 2) * 255f);
|
||||||
|
output[oo + 0] = 0; // b
|
||||||
|
output[oo + 1] = g; // g
|
||||||
|
output[oo + 2] = r; // r
|
||||||
|
output[oo + 3] = 255; // a
|
||||||
|
io += 4;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGBAHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RGBAHalfToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGBAHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
byte r = ClampByte(ToHalf(input, io + 0) * 255f);
|
||||||
|
byte g = ClampByte(ToHalf(input, io + 2) * 255f);
|
||||||
|
byte b = ClampByte(ToHalf(input, io + 4) * 255f);
|
||||||
|
byte a = ClampByte(ToHalf(input, io + 6) * 255f);
|
||||||
|
output[oo + 0] = b; // b
|
||||||
|
output[oo + 1] = g; // g
|
||||||
|
output[oo + 2] = r; // r
|
||||||
|
output[oo + 3] = a; // a
|
||||||
|
io += 8;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RFloatToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
byte r = ClampByte(ToSingle(input, io) * 255f);
|
||||||
|
output[oo + 0] = 0; // b
|
||||||
|
output[oo + 1] = 0; // g
|
||||||
|
output[oo + 2] = r; // r
|
||||||
|
output[oo + 3] = 255; // a
|
||||||
|
io += 4;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RGFloatToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
byte r = ClampByte(ToSingle(input, io + 0) * 255f);
|
||||||
|
byte g = ClampByte(ToSingle(input, io + 4) * 255f);
|
||||||
|
output[oo + 0] = 0; // b
|
||||||
|
output[oo + 1] = g; // g
|
||||||
|
output[oo + 2] = r; // r
|
||||||
|
output[oo + 3] = 255; // a
|
||||||
|
io += 8;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGBAFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RGBAFloatToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGBAFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
byte r = ClampByte(ToSingle(input, io + 0) * 255f);
|
||||||
|
byte g = ClampByte(ToSingle(input, io + 4) * 255f);
|
||||||
|
byte b = ClampByte(ToSingle(input, io + 8) * 255f);
|
||||||
|
byte a = ClampByte(ToSingle(input, io + 12) * 255f);
|
||||||
|
output[oo + 0] = b; // b
|
||||||
|
output[oo + 1] = g; // g
|
||||||
|
output[oo + 2] = r; // r
|
||||||
|
output[oo + 3] = a; // a
|
||||||
|
io += 16;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGB9e5FloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RGB9e5FloatToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGB9e5FloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
uint value = BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(io, 4));
|
||||||
|
double scale = Math.Pow(2, unchecked((int)(value >> 27) - 24));
|
||||||
|
byte r = ClampByte((value >> 0 & 0x1FF) * scale * 255.0);
|
||||||
|
byte g = ClampByte((value >> 9 & 0x1FF) * scale * 255.0);
|
||||||
|
byte b = ClampByte((value >> 18 & 0x1FF) * scale * 255.0);
|
||||||
|
output[oo + 0] = b; // b
|
||||||
|
output[oo + 1] = g; // g
|
||||||
|
output[oo + 2] = r; // r
|
||||||
|
output[oo + 3] = 255; // a
|
||||||
|
io += 4;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RG32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RG32ToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RG32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
output[oo + 0] = 0; // b
|
||||||
|
output[oo + 1] = input[io + 3]; // g
|
||||||
|
output[oo + 2] = input[io + 1]; // r
|
||||||
|
output[oo + 3] = 255; // a
|
||||||
|
io += 4;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGB48ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RGB48ToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGB48ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
output[oo + 0] = input[io + 5]; // b
|
||||||
|
output[oo + 1] = input[io + 3]; // g
|
||||||
|
output[oo + 2] = input[io + 1]; // r
|
||||||
|
output[oo + 3] = 255; // a
|
||||||
|
io += 6;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGBA64ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
|
{
|
||||||
|
output = new byte[width * height * 4];
|
||||||
|
return RGBA64ToBGRA32(input, width, height, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static int RGBA64ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||||
|
{
|
||||||
|
int io = 0;
|
||||||
|
int oo = 0;
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
output[oo + 0] = input[io + 5]; // b
|
||||||
|
output[oo + 1] = input[io + 3]; // g
|
||||||
|
output[oo + 2] = input[io + 1]; // r
|
||||||
|
output[oo + 3] = input[io + 7]; // a
|
||||||
|
io += 8;
|
||||||
|
oo += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private static float ToHalf(ReadOnlySpan<byte> input, int offset)
|
||||||
|
{
|
||||||
|
return (float)BinaryPrimitives.ReadHalfLittleEndian(input.Slice(offset, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private static float ToSingle(ReadOnlySpan<byte> input, int offset)
|
||||||
|
{
|
||||||
|
return BinaryPrimitives.ReadSingleLittleEndian(input.Slice(offset, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private static byte ClampByte(float x)
|
||||||
|
{
|
||||||
|
return byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? (byte)x : byte.MinValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private static byte ClampByte(double x)
|
||||||
|
{
|
||||||
|
return byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? (byte)x : byte.MinValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-18
@@ -3,7 +3,7 @@ using AssetRipper.TextureDecoder.Rgb.Formats;
|
|||||||
|
|
||||||
namespace AssetRipper.TextureDecoder.Tests
|
namespace AssetRipper.TextureDecoder.Tests
|
||||||
{
|
{
|
||||||
public sealed class NewRgbTests
|
public sealed class RgbConsistencyTests
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void ConvertA8Test()
|
public void ConvertA8Test()
|
||||||
@@ -12,7 +12,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = RgbConverter.A8ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
int bytesRead = OldRgbConverter.A8ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorA<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorA<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
@@ -40,7 +40,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = RgbConverter.RGB24ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
int bytesRead = OldRgbConverter.RGB24ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorRGB<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorRGB<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
@@ -55,7 +55,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = RgbConverter.RGBA32ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
int bytesRead = OldRgbConverter.RGBA32ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
@@ -70,7 +70,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = RgbConverter.ARGB32ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
int bytesRead = OldRgbConverter.ARGB32ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorARGB<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorARGB<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
@@ -85,7 +85,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = RgbConverter.RGB16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
int bytesRead = OldRgbConverter.RGB16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorRGB16, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorRGB16, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
@@ -126,7 +126,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = RgbConverter.RG16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
int bytesRead = OldRgbConverter.RG16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
@@ -141,7 +141,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = RgbConverter.R8ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
int bytesRead = OldRgbConverter.R8ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
@@ -153,7 +153,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
public void ConvertRHalfTest()
|
public void ConvertRHalfTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rhalf");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rhalf");
|
||||||
RgbConverter.RHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
OldRgbConverter.RHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorR<Half>, Half, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorR<Half>, Half, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
}
|
}
|
||||||
@@ -162,7 +162,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
public void ConvertRGHalfTest()
|
public void ConvertRGHalfTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rghalf");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rghalf");
|
||||||
RgbConverter.RGHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
OldRgbConverter.RGHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorRG<Half>, Half, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorRG<Half>, Half, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
}
|
}
|
||||||
@@ -171,7 +171,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
public void ConvertRGBAHalfTest()
|
public void ConvertRGBAHalfTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbahalf");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbahalf");
|
||||||
RgbConverter.RGBAHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
OldRgbConverter.RGBAHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorRGBA<Half>, Half, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorRGBA<Half>, Half, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
}
|
}
|
||||||
@@ -180,7 +180,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
public void ConvertRFloatTest()
|
public void ConvertRFloatTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rfloat");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rfloat");
|
||||||
RgbConverter.RFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
OldRgbConverter.RFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorR<float>, float, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorR<float>, float, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
}
|
}
|
||||||
@@ -189,7 +189,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
public void ConvertRGFloatTest()
|
public void ConvertRGFloatTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgfloat");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgfloat");
|
||||||
RgbConverter.RGFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
OldRgbConverter.RGFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorRG<float>, float, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorRG<float>, float, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
}
|
}
|
||||||
@@ -198,7 +198,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
public void ConvertRGBAFloatTest()
|
public void ConvertRGBAFloatTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbafloat");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbafloat");
|
||||||
RgbConverter.RGBAFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
OldRgbConverter.RGBAFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorRGBA<float>, float, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorRGBA<float>, float, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
}
|
}
|
||||||
@@ -207,7 +207,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
public void ConvertRGB9e5FloatTest()
|
public void ConvertRGB9e5FloatTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb9e5float");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb9e5float");
|
||||||
RgbConverter.RGB9e5FloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
OldRgbConverter.RGB9e5FloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
}
|
}
|
||||||
@@ -216,7 +216,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
public void ConvertRG32Test()
|
public void ConvertRG32Test()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rg32");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rg32");
|
||||||
RgbConverter.RG32ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
OldRgbConverter.RG32ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorRG<ushort>, ushort, ColorBGRA<byte>, byte>(data, 512, 512, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorRG<ushort>, ushort, ColorBGRA<byte>, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
}
|
}
|
||||||
@@ -225,7 +225,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
public void ConvertRGB48Test()
|
public void ConvertRGB48Test()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb48");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb48");
|
||||||
RgbConverter.RGB48ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
OldRgbConverter.RGB48ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorRGB<ushort>, ushort, ColorBGRA<byte>, byte>(data, 512, 512, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorRGB<ushort>, ushort, ColorBGRA<byte>, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
}
|
}
|
||||||
@@ -234,7 +234,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
|||||||
public void ConvertRGBA64Test()
|
public void ConvertRGBA64Test()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgba64");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgba64");
|
||||||
RgbConverter.RGBA64ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
OldRgbConverter.RGBA64ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||||
RgbConverter.Convert<ColorRGBA<ushort>, ushort, ColorBGRA<byte>, byte>(data, 512, 512, out byte[] newDecodedData);
|
RgbConverter.Convert<ColorRGBA<ushort>, ushort, ColorBGRA<byte>, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
namespace AssetRipper.TextureDecoder.Tests
|
using AssetRipper.TextureDecoder.Rgb;
|
||||||
|
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||||
|
|
||||||
|
namespace AssetRipper.TextureDecoder.Tests
|
||||||
{
|
{
|
||||||
public sealed class RgbTests
|
public sealed class RgbTests
|
||||||
{
|
{
|
||||||
@@ -9,7 +12,7 @@
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = Rgb.RgbConverter.A8ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
int bytesRead = ConvertSquare<ColorA<byte>, byte>(data.Slice(totalBytesRead), size);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
}
|
}
|
||||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||||
@@ -22,7 +25,7 @@
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = Rgb.RgbConverter.RGB24ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
int bytesRead = ConvertSquare<ColorRGB<byte>, byte>(data.Slice(totalBytesRead), size);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
}
|
}
|
||||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||||
@@ -35,7 +38,7 @@
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = Rgb.RgbConverter.RGBA32ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
int bytesRead = ConvertSquare<ColorRGBA<byte>, byte>(data.Slice(totalBytesRead), size);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
}
|
}
|
||||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||||
@@ -48,7 +51,7 @@
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = Rgb.RgbConverter.ARGB32ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
int bytesRead = ConvertSquare<ColorARGB<byte>, byte>(data.Slice(totalBytesRead), size);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
}
|
}
|
||||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||||
@@ -61,7 +64,7 @@
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = Rgb.RgbConverter.RGB16ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
int bytesRead = ConvertSquare<ColorRGB16, byte>(data.Slice(totalBytesRead), size);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
}
|
}
|
||||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||||
@@ -74,7 +77,7 @@
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = Rgb.RgbConverter.RG16ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
int bytesRead = ConvertSquare<ColorRG<byte>, byte>(data.Slice(totalBytesRead), size);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
}
|
}
|
||||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||||
@@ -87,7 +90,7 @@
|
|||||||
int totalBytesRead = 0;
|
int totalBytesRead = 0;
|
||||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||||
{
|
{
|
||||||
int bytesRead = Rgb.RgbConverter.R8ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
int bytesRead = ConvertSquare<ColorR<byte>, byte>(data.Slice(totalBytesRead), size);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
}
|
}
|
||||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||||
@@ -97,7 +100,7 @@
|
|||||||
public void ConvertRHalfTest()
|
public void ConvertRHalfTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rhalf");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rhalf");
|
||||||
int bytesRead = Rgb.RgbConverter.RHalfToBGRA32(data, 256, 256, out _);
|
int bytesRead = ConvertSquare<ColorR<Half>, Half>(data, 256);
|
||||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +108,7 @@
|
|||||||
public void ConvertRGHalfTest()
|
public void ConvertRGHalfTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rghalf");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rghalf");
|
||||||
int bytesRead = Rgb.RgbConverter.RGHalfToBGRA32(data, 256, 256, out _);
|
int bytesRead = ConvertSquare<ColorRG<Half>, Half>(data, 256);
|
||||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +116,7 @@
|
|||||||
public void ConvertRGBAHalfTest()
|
public void ConvertRGBAHalfTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbahalf");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbahalf");
|
||||||
int bytesRead = Rgb.RgbConverter.RGBAHalfToBGRA32(data, 256, 256, out _);
|
int bytesRead = ConvertSquare<ColorRGBA<Half>, Half>(data, 256);
|
||||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +124,7 @@
|
|||||||
public void ConvertRFloatTest()
|
public void ConvertRFloatTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rfloat");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rfloat");
|
||||||
int bytesRead = Rgb.RgbConverter.RFloatToBGRA32(data, 256, 256, out _);
|
int bytesRead = ConvertSquare<ColorR<float>, float>(data, 256);
|
||||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +132,7 @@
|
|||||||
public void ConvertRGFloatTest()
|
public void ConvertRGFloatTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgfloat");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgfloat");
|
||||||
int bytesRead = Rgb.RgbConverter.RGFloatToBGRA32(data, 256, 256, out _);
|
int bytesRead = ConvertSquare<ColorRG<float>, float>(data, 256);
|
||||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +140,7 @@
|
|||||||
public void ConvertRGBAFloatTest()
|
public void ConvertRGBAFloatTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbafloat");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbafloat");
|
||||||
int bytesRead = Rgb.RgbConverter.RGBAFloatToBGRA32(data, 256, 256, out _);
|
int bytesRead = ConvertSquare<ColorRGBA<float>, float>(data, 256);
|
||||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +148,7 @@
|
|||||||
public void ConvertRGB9e5FloatTest()
|
public void ConvertRGB9e5FloatTest()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb9e5float");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb9e5float");
|
||||||
int bytesRead = Rgb.RgbConverter.RGB9e5FloatToBGRA32(data, 256, 256, out _);
|
int bytesRead = ConvertSquare<ColorRGB9e5, double>(data, 256);
|
||||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +156,7 @@
|
|||||||
public void ConvertRG32Test()
|
public void ConvertRG32Test()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rg32");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rg32");
|
||||||
int bytesRead = Rgb.RgbConverter.RG32ToBGRA32(data, 512, 512, out _);
|
int bytesRead = ConvertSquare<ColorRG<ushort>, ushort>(data, 512);
|
||||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +164,7 @@
|
|||||||
public void ConvertRGB48Test()
|
public void ConvertRGB48Test()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb48");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb48");
|
||||||
int bytesRead = Rgb.RgbConverter.RGB48ToBGRA32(data, 512, 512, out _);
|
int bytesRead = ConvertSquare<ColorRGB<ushort>, ushort>(data, 512);
|
||||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,8 +172,15 @@
|
|||||||
public void ConvertRGBA64Test()
|
public void ConvertRGBA64Test()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgba64");
|
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgba64");
|
||||||
int bytesRead = Rgb.RgbConverter.RGBA64ToBGRA32(data, 512, 512, out _);
|
int bytesRead = ConvertSquare<ColorRGBA<ushort>, ushort>(data, 512);
|
||||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int ConvertSquare<TColor, TChannel>(ReadOnlySpan<byte> data, int size)
|
||||||
|
where TColor : unmanaged, IColor<TChannel>
|
||||||
|
where TChannel : unmanaged
|
||||||
|
{
|
||||||
|
return RgbConverter.Convert<TColor, TChannel, ColorBGRA<byte>, byte>(data, size, size, out _);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
<Folder Name="/Solution Items/Workflows/">
|
<Folder Name="/Solution Items/Workflows/">
|
||||||
<File Path=".github/workflows/build.yml" />
|
<File Path=".github/workflows/build.yml" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Project Path="AssetRipper.TextureDecoder.Benchmarks/AssetRipper.TextureDecoder.Benchmarks.csproj" />
|
|
||||||
<Project Path="AssetRipper.TextureDecoder.ColorGenerator/AssetRipper.TextureDecoder.ColorGenerator.csproj" />
|
<Project Path="AssetRipper.TextureDecoder.ColorGenerator/AssetRipper.TextureDecoder.ColorGenerator.csproj" />
|
||||||
<Project Path="AssetRipper.TextureDecoder.ConsoleApp/AssetRipper.TextureDecoder.ConsoleApp.csproj" />
|
<Project Path="AssetRipper.TextureDecoder.ConsoleApp/AssetRipper.TextureDecoder.ConsoleApp.csproj" />
|
||||||
<Project Path="AssetRipper.TextureDecoder.Paths/AssetRipper.TextureDecoder.Paths.csproj" />
|
<Project Path="AssetRipper.TextureDecoder.Paths/AssetRipper.TextureDecoder.Paths.csproj" />
|
||||||
@@ -1,26 +1,25 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<IsTrimmable>true</IsTrimmable>
|
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
|
||||||
<PackageId>AssetRipper.TextureDecoder</PackageId>
|
<PackageId>AssetRipper.TextureDecoder</PackageId>
|
||||||
<PackageTags>C# Texture</PackageTags>
|
<PackageTags>C# Texture</PackageTags>
|
||||||
<RepositoryUrl>https://github.com/AssetRipper/TextureDecoder</RepositoryUrl>
|
<RepositoryUrl>https://github.com/AssetRipper/TextureDecoder</RepositoryUrl>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PackageProjectUrl>https://github.com/AssetRipper/TextureDecoder</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/AssetRipper/TextureDecoder</PackageProjectUrl>
|
||||||
<Copyright>Copyright (c) 2022 - 2024 ds5678</Copyright>
|
<Copyright>Copyright (c) 2022 - 2026 ds5678</Copyright>
|
||||||
<Description>Managed library for decoding textures in multiple formats.</Description>
|
<Description>Managed library for decoding textures in multiple formats.</Description>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<DocumentationFile>bin\TextureDecoder.xml</DocumentationFile>
|
|
||||||
<NoWarn>1591</NoWarn>
|
<NoWarn>1591</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.400">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
|||||||
where TOutputChannelValue : unmanaged
|
where TOutputChannelValue : unmanaged
|
||||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||||
{
|
{
|
||||||
output = new byte[width * height * 4];
|
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||||
return DecodeASTC<TOutputColor, TOutputChannelValue>(input, width, height, blockWidth, blockHeight, output);
|
return DecodeASTC<TOutputColor, TOutputChannelValue>(input, width, height, blockWidth, blockHeight, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,10 +66,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
|||||||
if (input[0] == 0xfc && (input[1] & 1) == 1)
|
if (input[0] == 0xfc && (input[1] & 1) == 1)
|
||||||
{
|
{
|
||||||
ColorRGBA<byte> c = new(input[9], input[11], input[13], input[15]);
|
ColorRGBA<byte> c = new(input[9], input[11], input[13], input[15]);
|
||||||
for (int i = 0; i < blockWidth * blockHeight; i++)
|
output[..(blockWidth * blockHeight)].Fill(c);
|
||||||
{
|
|
||||||
output[i] = c;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -886,6 +883,11 @@ namespace AssetRipper.TextureDecoder.Astc
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private static int GetBits(ReadOnlySpan<byte> input, int bit, int len)
|
private static int GetBits(ReadOnlySpan<byte> input, int bit, int len)
|
||||||
{
|
{
|
||||||
|
if (bit < 0 || input.Length * 8 < bit + len)
|
||||||
|
{
|
||||||
|
// Invalid input, but we should return something instead of throwing an exception
|
||||||
|
return default;
|
||||||
|
}
|
||||||
ReadOnlySpan<byte> slice = input[(bit / 8)..];
|
ReadOnlySpan<byte> slice = input[(bit / 8)..];
|
||||||
int bitBuffer;
|
int bitBuffer;
|
||||||
if (slice.Length >= sizeof(int))
|
if (slice.Length >= sizeof(int))
|
||||||
|
|||||||
@@ -76,16 +76,16 @@ internal static class BinaryWriterExtensions
|
|||||||
switch (Unsafe.SizeOf<T>())
|
switch (Unsafe.SizeOf<T>())
|
||||||
{
|
{
|
||||||
case sizeof(byte):
|
case sizeof(byte):
|
||||||
writer.Write(Unsafe.As<T, byte>(ref value));
|
writer.Write(Unsafe.BitCast<T, byte>(value));
|
||||||
break;
|
break;
|
||||||
case sizeof(ushort):
|
case sizeof(ushort):
|
||||||
writer.Write(Unsafe.As<T, ushort>(ref value));
|
writer.Write(Unsafe.BitCast<T, ushort>(value));
|
||||||
break;
|
break;
|
||||||
case sizeof(uint):
|
case sizeof(uint):
|
||||||
writer.Write(Unsafe.As<T, uint>(ref value));
|
writer.Write(Unsafe.BitCast<T, uint>(value));
|
||||||
break;
|
break;
|
||||||
case sizeof(ulong):
|
case sizeof(ulong):
|
||||||
writer.Write(Unsafe.As<T, ulong>(ref value));
|
writer.Write(Unsafe.BitCast<T, ulong>(value));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ public readonly record struct ExrChannelList(ExrChannel[] Channels) : IExrDataTy
|
|||||||
{
|
{
|
||||||
public static ReadOnlySpan<byte> TypeName => "chlist"u8;
|
public static ReadOnlySpan<byte> TypeName => "chlist"u8;
|
||||||
|
|
||||||
public int Size => Channels.Sum(c => c.Size);
|
public int Size => Channels.Sum(c => c.Size) + sizeof(byte);
|
||||||
|
|
||||||
public void Write(BinaryWriter writer)
|
public void Write(BinaryWriter writer)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ public static class ExrWriter
|
|||||||
for (int i = 0; i < height; i++)
|
for (int i = 0; i < height; i++)
|
||||||
{
|
{
|
||||||
writer.Write(offset);
|
writer.Write(offset);
|
||||||
offset += sizeof(int) + ChannelCount * ChannelSize * width;
|
offset += sizeof(int) + sizeof(int) + ChannelCount * ChannelSize * width;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Scan lines
|
//Scan lines
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public static class Color
|
|||||||
{
|
{
|
||||||
if (typeof(TThis) == typeof(TTarget))
|
if (typeof(TThis) == typeof(TTarget))
|
||||||
{
|
{
|
||||||
return Unsafe.As<TThis, TTarget>(ref color);
|
return Unsafe.BitCast<TThis, TTarget>(color);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -92,4 +92,30 @@ public static class Color
|
|||||||
return destination;
|
return destination;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static TColor SRgbToLinear<TColor, TChannel>(this TColor color)
|
||||||
|
where TChannel : unmanaged
|
||||||
|
where TColor : unmanaged, IColor<TChannel>
|
||||||
|
{
|
||||||
|
color.GetChannels(out TChannel r, out TChannel g, out TChannel b, out TChannel a);
|
||||||
|
color.SetChannels(
|
||||||
|
SRgb.ToLinear(r),
|
||||||
|
SRgb.ToLinear(g),
|
||||||
|
SRgb.ToLinear(b),
|
||||||
|
a);
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static TColor LinearToSRgb<TColor, TChannel>(this TColor color)
|
||||||
|
where TChannel : unmanaged
|
||||||
|
where TColor : unmanaged, IColor<TChannel>
|
||||||
|
{
|
||||||
|
color.GetChannels(out TChannel r, out TChannel g, out TChannel b, out TChannel a);
|
||||||
|
color.SetChannels(
|
||||||
|
SRgb.FromLinear(r),
|
||||||
|
SRgb.FromLinear(g),
|
||||||
|
SRgb.FromLinear(b),
|
||||||
|
a);
|
||||||
|
return color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ public static partial class NumericConversion
|
|||||||
|
|
||||||
TFrom SignBit = TFrom.One << (Unsafe.SizeOf<TFrom>() * 8 - 1);
|
TFrom SignBit = TFrom.One << (Unsafe.SizeOf<TFrom>() * 8 - 1);
|
||||||
TFrom converted = (SignBit ^ value);
|
TFrom converted = (SignBit ^ value);
|
||||||
return Unsafe.As<TFrom, TTo>(ref converted);
|
return Unsafe.BitCast<TFrom, TTo>(converted);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -56,6 +56,6 @@ public static partial class NumericConversion
|
|||||||
}
|
}
|
||||||
|
|
||||||
TTo SignBit = TTo.One << (Unsafe.SizeOf<TTo>() * 8 - 1);
|
TTo SignBit = TTo.One << (Unsafe.SizeOf<TTo>() * 8 - 1);
|
||||||
return SignBit ^ Unsafe.As<TFrom, TTo>(ref value);
|
return SignBit ^ Unsafe.BitCast<TFrom, TTo>(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -1,490 +1,7 @@
|
|||||||
using System.Buffers.Binary;
|
|
||||||
|
|
||||||
namespace AssetRipper.TextureDecoder.Rgb
|
namespace AssetRipper.TextureDecoder.Rgb
|
||||||
{
|
{
|
||||||
public static class RgbConverter
|
public static class RgbConverter
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int A8ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return A8ToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int A8ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
output[oo + 0] = 0; // b
|
|
||||||
output[oo + 1] = 0; // g
|
|
||||||
output[oo + 2] = 0; // r
|
|
||||||
output[oo + 3] = input[io]; // a
|
|
||||||
oo += 4;
|
|
||||||
io++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGB24ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RGB24ToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGB24ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
output[oo + 0] = input[io + 2]; // b
|
|
||||||
output[oo + 1] = input[io + 1]; // g
|
|
||||||
output[oo + 2] = input[io + 0]; // r
|
|
||||||
output[oo + 3] = 255; // a
|
|
||||||
io += 3;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGBA32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RGBA32ToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGBA32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
output[oo + 0] = input[io + 2]; // b
|
|
||||||
output[oo + 1] = input[io + 1]; // g
|
|
||||||
output[oo + 2] = input[io + 0]; // r
|
|
||||||
output[oo + 3] = input[io + 3]; // a
|
|
||||||
io += 4;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int ARGB32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return ARGB32ToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int ARGB32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
output[oo + 0] = input[io + 3]; // b
|
|
||||||
output[oo + 1] = input[io + 2]; // g
|
|
||||||
output[oo + 2] = input[io + 1]; // r
|
|
||||||
output[oo + 3] = input[io + 0]; // a
|
|
||||||
io += 4;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGB16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RGB16ToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGB16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
byte r = (byte)(input[io + 1] & 0xF8);
|
|
||||||
byte g = unchecked((byte)((input[io + 1] << 5) | ((input[io + 0] & 0xE0) >> 3)));
|
|
||||||
byte b = unchecked((byte)(input[io + 0] << 3));
|
|
||||||
output[oo + 0] = b; // b
|
|
||||||
output[oo + 1] = g; // g
|
|
||||||
output[oo + 2] = r; // r
|
|
||||||
output[oo + 3] = 255; // a
|
|
||||||
io += 2;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RG16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RG16ToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RG16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
output[oo + 0] = 0; // b
|
|
||||||
output[oo + 1] = input[io + 1]; // g
|
|
||||||
output[oo + 2] = input[io + 0]; // r
|
|
||||||
output[oo + 3] = 255; // a
|
|
||||||
io += 2;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int R8ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return R8ToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int R8ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
output[oo + 0] = 0; // b
|
|
||||||
output[oo + 1] = 0; // g
|
|
||||||
output[oo + 2] = input[io + 0]; // r
|
|
||||||
output[oo + 3] = 255; // a
|
|
||||||
io += 1;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RHalfToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
byte r = ClampByte(ToHalf(input, io) * 255f);
|
|
||||||
output[oo + 0] = 0; // b
|
|
||||||
output[oo + 1] = 0; // g
|
|
||||||
output[oo + 2] = r; // r
|
|
||||||
output[oo + 3] = 255; // a
|
|
||||||
io += 2;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RGHalfToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
byte r = ClampByte(ToHalf(input, io + 0) * 255f);
|
|
||||||
byte g = ClampByte(ToHalf(input, io + 2) * 255f);
|
|
||||||
output[oo + 0] = 0; // b
|
|
||||||
output[oo + 1] = g; // g
|
|
||||||
output[oo + 2] = r; // r
|
|
||||||
output[oo + 3] = 255; // a
|
|
||||||
io += 4;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGBAHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RGBAHalfToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGBAHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
byte r = ClampByte(ToHalf(input, io + 0) * 255f);
|
|
||||||
byte g = ClampByte(ToHalf(input, io + 2) * 255f);
|
|
||||||
byte b = ClampByte(ToHalf(input, io + 4) * 255f);
|
|
||||||
byte a = ClampByte(ToHalf(input, io + 6) * 255f);
|
|
||||||
output[oo + 0] = b; // b
|
|
||||||
output[oo + 1] = g; // g
|
|
||||||
output[oo + 2] = r; // r
|
|
||||||
output[oo + 3] = a; // a
|
|
||||||
io += 8;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RFloatToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
byte r = ClampByte(ToSingle(input, io) * 255f);
|
|
||||||
output[oo + 0] = 0; // b
|
|
||||||
output[oo + 1] = 0; // g
|
|
||||||
output[oo + 2] = r; // r
|
|
||||||
output[oo + 3] = 255; // a
|
|
||||||
io += 4;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RGFloatToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
byte r = ClampByte(ToSingle(input, io + 0) * 255f);
|
|
||||||
byte g = ClampByte(ToSingle(input, io + 4) * 255f);
|
|
||||||
output[oo + 0] = 0; // b
|
|
||||||
output[oo + 1] = g; // g
|
|
||||||
output[oo + 2] = r; // r
|
|
||||||
output[oo + 3] = 255; // a
|
|
||||||
io += 8;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGBAFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RGBAFloatToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGBAFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
byte r = ClampByte(ToSingle(input, io + 0) * 255f);
|
|
||||||
byte g = ClampByte(ToSingle(input, io + 4) * 255f);
|
|
||||||
byte b = ClampByte(ToSingle(input, io + 8) * 255f);
|
|
||||||
byte a = ClampByte(ToSingle(input, io + 12) * 255f);
|
|
||||||
output[oo + 0] = b; // b
|
|
||||||
output[oo + 1] = g; // g
|
|
||||||
output[oo + 2] = r; // r
|
|
||||||
output[oo + 3] = a; // a
|
|
||||||
io += 16;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGB9e5FloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RGB9e5FloatToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGB9e5FloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
uint value = BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(io, 4));
|
|
||||||
double scale = Math.Pow(2, unchecked((int)(value >> 27) - 24));
|
|
||||||
byte r = ClampByte((value >> 0 & 0x1FF) * scale * 255.0);
|
|
||||||
byte g = ClampByte((value >> 9 & 0x1FF) * scale * 255.0);
|
|
||||||
byte b = ClampByte((value >> 18 & 0x1FF) * scale * 255.0);
|
|
||||||
output[oo + 0] = b; // b
|
|
||||||
output[oo + 1] = g; // g
|
|
||||||
output[oo + 2] = r; // r
|
|
||||||
output[oo + 3] = 255; // a
|
|
||||||
io += 4;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RG32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RG32ToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RG32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
output[oo + 0] = 0; // b
|
|
||||||
output[oo + 1] = input[io + 3]; // g
|
|
||||||
output[oo + 2] = input[io + 1]; // r
|
|
||||||
output[oo + 3] = 255; // a
|
|
||||||
io += 4;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGB48ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RGB48ToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGB48ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
output[oo + 0] = input[io + 5]; // b
|
|
||||||
output[oo + 1] = input[io + 3]; // g
|
|
||||||
output[oo + 2] = input[io + 1]; // r
|
|
||||||
output[oo + 3] = 255; // a
|
|
||||||
io += 6;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGBA64ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
|
||||||
{
|
|
||||||
output = new byte[width * height * 4];
|
|
||||||
return RGBA64ToBGRA32(input, width, height, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
public static int RGBA64ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
|
||||||
{
|
|
||||||
int io = 0;
|
|
||||||
int oo = 0;
|
|
||||||
for (int i = 0; i < width; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
{
|
|
||||||
output[oo + 0] = input[io + 5]; // b
|
|
||||||
output[oo + 1] = input[io + 3]; // g
|
|
||||||
output[oo + 2] = input[io + 1]; // r
|
|
||||||
output[oo + 3] = input[io + 7]; // a
|
|
||||||
io += 8;
|
|
||||||
oo += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return io;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static int Convert<TSourceColor, TSourceChannel, TDestinationColor, TDestinationChannel>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
public static int Convert<TSourceColor, TSourceChannel, TDestinationColor, TDestinationChannel>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||||
where TSourceChannel : unmanaged
|
where TSourceChannel : unmanaged
|
||||||
@@ -548,28 +65,24 @@ namespace AssetRipper.TextureDecoder.Rgb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
public static void SRgbToLinear<TColor, TChannel>(Span<TColor> span)
|
||||||
private static float ToHalf(ReadOnlySpan<byte> input, int offset)
|
where TChannel : unmanaged
|
||||||
|
where TColor : unmanaged, IColor<TChannel>
|
||||||
{
|
{
|
||||||
return (float)BinaryPrimitives.ReadHalfLittleEndian(input.Slice(offset, 2));
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
span[i] = span[i].SRgbToLinear<TColor, TChannel>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
public static void LinearToSRgb<TColor, TChannel>(Span<TColor> span)
|
||||||
private static float ToSingle(ReadOnlySpan<byte> input, int offset)
|
where TChannel : unmanaged
|
||||||
|
where TColor : unmanaged, IColor<TChannel>
|
||||||
{
|
{
|
||||||
return BinaryPrimitives.ReadSingleLittleEndian(input.Slice(offset, 4));
|
for (int i = 0; i < span.Length; i++)
|
||||||
}
|
{
|
||||||
|
span[i] = span[i].LinearToSRgb<TColor, TChannel>();
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
}
|
||||||
private static byte ClampByte(float x)
|
|
||||||
{
|
|
||||||
return byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? (byte)x : byte.MinValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
private static byte ClampByte(double x)
|
|
||||||
{
|
|
||||||
return byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? (byte)x : byte.MinValue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
namespace AssetRipper.TextureDecoder.Rgb;
|
||||||
|
|
||||||
|
internal static class SRgb
|
||||||
|
{
|
||||||
|
public static T ToLinear<T>(T value) where T : unmanaged
|
||||||
|
{
|
||||||
|
double d = NumericConversion.Convert<T, double>(value);
|
||||||
|
d = SRgbToLinear(d);
|
||||||
|
return NumericConversion.Convert<double, T>(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T FromLinear<T>(T value) where T : unmanaged
|
||||||
|
{
|
||||||
|
double d = NumericConversion.Convert<T, double>(value);
|
||||||
|
d = LinearToSRgb(d);
|
||||||
|
return NumericConversion.Convert<double, T>(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double SRgbToLinear(double value)
|
||||||
|
{
|
||||||
|
if (value <= 0.04045)
|
||||||
|
{
|
||||||
|
return value / 12.92;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return double.Pow((value + 0.055) / 1.055, 2.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double LinearToSRgb(double value)
|
||||||
|
{
|
||||||
|
if (value <= 0.0031308)
|
||||||
|
{
|
||||||
|
return value * 12.92;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1.055 * double.Pow(value, 1.0 / 2.4) - 0.055;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<LangVersion>Latest</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<IsAotCompatible>true</IsAotCompatible>
|
<IsAotCompatible>true</IsAotCompatible>
|
||||||
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||||
|
|
||||||
<VersionPrefix>2.5.0</VersionPrefix>
|
<VersionPrefix>2.6.3</VersionPrefix>
|
||||||
<Copyright>Copyright © ds5678</Copyright>
|
<Copyright>Copyright © ds5678</Copyright>
|
||||||
<Authors>ds5678</Authors>
|
<Authors>ds5678</Authors>
|
||||||
<Company>AssetRipper</Company>
|
<Company>AssetRipper</Company>
|
||||||
|
|||||||
@@ -8,17 +8,18 @@ internal class Program
|
|||||||
{
|
{
|
||||||
int originalBitCount = int.Parse(args[0]);
|
int originalBitCount = int.Parse(args[0]);
|
||||||
int newBitCount = int.Parse(args[1]);
|
int newBitCount = int.Parse(args[1]);
|
||||||
EstimateConstants(originalBitCount, newBitCount, out uint a, out uint b, out int c);
|
bool perfectMatch = EstimateConstants(originalBitCount, newBitCount, out uint a, out long b, out int c);
|
||||||
Console.WriteLine("Done!");
|
Console.WriteLine("Done!");
|
||||||
Console.WriteLine($"Result: ({a} * x + {b}) >> {c}");
|
Console.WriteLine($"Result: ({a} * x + {b}) >> {c}");
|
||||||
|
Console.WriteLine($"Perfect match: {perfectMatch}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EstimateConstants(int originalBitCount, int newBitCount, out uint a, out uint b, out int c)
|
private static bool EstimateConstants(int originalBitCount, int newBitCount, out uint a, out long b, out int c)
|
||||||
{
|
{
|
||||||
a = default;
|
a = default;
|
||||||
b = default;
|
b = default;
|
||||||
c = default;
|
c = default;
|
||||||
float bestError = float.MaxValue;//Sum of squared errors
|
double bestError = double.MaxValue;//Sum of squared errors
|
||||||
uint originalMaxValue = GetIntegerMaxValue(originalBitCount);
|
uint originalMaxValue = GetIntegerMaxValue(originalBitCount);
|
||||||
uint newMaxValue = GetIntegerMaxValue(newBitCount);
|
uint newMaxValue = GetIntegerMaxValue(newBitCount);
|
||||||
int bitDiff = newBitCount - originalBitCount;
|
int bitDiff = newBitCount - originalBitCount;
|
||||||
@@ -27,55 +28,94 @@ internal class Program
|
|||||||
//If bitDiff is negative, it means we are going from large numbers to small numbers.
|
//If bitDiff is negative, it means we are going from large numbers to small numbers.
|
||||||
//In other words, we will always have to shift right by at least enough to overcome the bit surplus.
|
//In other words, we will always have to shift right by at least enough to overcome the bit surplus.
|
||||||
int minc = int.Max(0, -bitDiff);
|
int minc = int.Max(0, -bitDiff);
|
||||||
|
// We are limited to a 32-bit result, so we cannot shift left so much that we exceed 32 bits.
|
||||||
int maxc = 32 - newBitCount;
|
int maxc = 32 - newBitCount;
|
||||||
|
|
||||||
for (int ic = minc; ic <= maxc; ic++)
|
for (int ic = minc; ic <= maxc; ic++)
|
||||||
{
|
{
|
||||||
Console.WriteLine(ic);
|
Console.WriteLine(ic);
|
||||||
|
|
||||||
//a: 2 ^ (c + bitDiff) to 2 ^ (c + bitDiff + 1)
|
//a: 2^c * newMaxValue / originalMaxValue rounded to nearest integer
|
||||||
uint mina = Pow2(ic + bitDiff);
|
double ia_exact = (double)Pow2(ic) * newMaxValue / originalMaxValue;
|
||||||
uint maxa = Pow2(ic + bitDiff + 1);
|
|
||||||
|
|
||||||
//b: 0 to 2^c - 1
|
ReadOnlySpan<uint> ia_values = ia_exact < 1 ? [(uint)double.Ceiling(ia_exact)] : [(uint)double.Floor(ia_exact), (uint)double.Ceiling(ia_exact)];
|
||||||
//Any bigger and it will cause 0 not to correspond with 0.
|
|
||||||
uint minb = 0;
|
|
||||||
uint maxb = Pow2(ic) - 1;
|
|
||||||
|
|
||||||
for (uint ia = mina; ia <= maxa; ia++)
|
foreach (uint ia in ia_values)
|
||||||
{
|
{
|
||||||
if (ConvertEstimate(originalMaxValue, ia, minb, ic) > newMaxValue || ConvertEstimate(originalMaxValue, ia, maxb, ic) < newMaxValue)
|
//b: 0 to 2^c - 1
|
||||||
|
//Any bigger and it will cause 0 not to correspond with 0.
|
||||||
|
uint minb;
|
||||||
|
uint maxb;
|
||||||
{
|
{
|
||||||
continue;
|
//We can restrict b further by ensuring that originalMaxValue maps to newMaxValue.
|
||||||
|
long minb_one = (long)(newMaxValue << ic) - (ia * originalMaxValue);
|
||||||
|
long maxb_one = minb_one + Pow2(ic) - 1;
|
||||||
|
|
||||||
|
minb = (uint)long.Max(0, minb_one);
|
||||||
|
maxb = (uint)long.Min(Pow2(ic) - 1, long.Max(0, maxb_one));
|
||||||
|
minb = uint.Min(minb, maxb);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint ib = minb; ib <= maxb; ib++)
|
//Errors for b are nearly parabolic, so we try to find the minimum by ternary search.
|
||||||
|
//This is not guaranteed to find the absolute minimum since there's noise in the error, but it should get close enough.
|
||||||
|
uint current_b_low = minb;
|
||||||
|
uint current_b_high = maxb;
|
||||||
|
while (current_b_high - current_b_low > 3)
|
||||||
{
|
{
|
||||||
if (ConvertEstimate(originalMaxValue, ia, ib, ic) == newMaxValue)
|
uint range = current_b_high - current_b_low;
|
||||||
|
uint b1 = current_b_low + range / 3;
|
||||||
|
uint b2 = current_b_high - range / 3;
|
||||||
|
double error1 = CalculateMeanSquaredError(ia, b1, ic, originalMaxValue, newMaxValue, out _);
|
||||||
|
double error2 = CalculateMeanSquaredError(ia, b2, ic, originalMaxValue, newMaxValue, out _);
|
||||||
|
if (error1 < error2)
|
||||||
{
|
{
|
||||||
float error = 0;
|
current_b_high = b2;
|
||||||
for (uint x = 1; x < originalMaxValue; x++)
|
}
|
||||||
{
|
else
|
||||||
uint y = ConvertEstimate(x, ia, ib, ic);
|
{
|
||||||
float yExact = (float)x * newMaxValue / originalMaxValue;
|
current_b_low = b1;
|
||||||
float diff = yExact - y;
|
}
|
||||||
error += diff * diff;
|
}
|
||||||
}
|
|
||||||
if (error < bestError)
|
for (uint ib = current_b_low; ib <= current_b_high; ib++)
|
||||||
{
|
{
|
||||||
a = ia;
|
double error = CalculateMeanSquaredError(ia, ib, ic, originalMaxValue, newMaxValue, out bool anyIncorrect);
|
||||||
b = ib;
|
if (error < bestError)
|
||||||
c = ic;
|
{
|
||||||
if (error == 0)//Huge performance savings for when newBitCount / originalBitCount has no remainder.
|
bestError = error;
|
||||||
{
|
a = ia;
|
||||||
return;
|
b = ib;
|
||||||
}
|
c = ic;
|
||||||
bestError = error;
|
Console.WriteLine($"New best: a={a}, b={b}, c={c}, MSE={bestError}");
|
||||||
}
|
}
|
||||||
|
if (!anyIncorrect)
|
||||||
|
{
|
||||||
|
//Perfect match
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double CalculateMeanSquaredError(uint a, uint b, int c, uint originalMaxValue, uint newMaxValue, out bool anyIncorrect)
|
||||||
|
{
|
||||||
|
double meanSquaredError = 0;
|
||||||
|
anyIncorrect = false;
|
||||||
|
for (uint x = 0; x <= originalMaxValue; x++)
|
||||||
|
{
|
||||||
|
uint y = ConvertEstimate(x, a, b, c);
|
||||||
|
double yExact = (double)x * newMaxValue / originalMaxValue;
|
||||||
|
|
||||||
|
double diff = yExact - y;
|
||||||
|
meanSquaredError = meanSquaredError * x / (x + 1) + (diff * diff) / (x + 1);
|
||||||
|
|
||||||
|
uint yExactInt = (uint)double.Round(yExact, MidpointRounding.AwayFromZero);
|
||||||
|
anyIncorrect |= (y != yExactInt);
|
||||||
|
}
|
||||||
|
return meanSquaredError;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
|||||||
Reference in new issue
Block a user