mirror of
https://github.com/AssetRipper/AssetRipper.TextureDecoder.git
synced 2026-11-13 02:21:29 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e41eeb5766 | ||
|
|
a7a12fdd72 | ||
|
|
801edea4cf | ||
|
|
3cb859c6ac | ||
|
|
0738ba4b50 | ||
|
|
09067cecbb | ||
|
|
ec6754a5d2 | ||
|
|
bc4d0f5378 | ||
|
|
8f0f94695b | ||
|
|
aacbfbd222 | ||
|
|
93fe6948b6 | ||
|
|
0e6f3080be | ||
|
|
f8294eff8b | ||
|
|
0b4d1dfc83 | ||
|
|
f5c1aca0e1 | ||
|
|
d4d335d94b | ||
|
|
70775daca9 | ||
|
|
3a94ea1a0f | ||
|
|
db875125d2 | ||
|
|
cdd73752df | ||
|
|
99556c46dd | ||
|
|
c919852137 | ||
|
|
9bca952f2d | ||
|
|
dbfe5b2979 | ||
|
|
168098c29e | ||
|
|
8b493e5650 | ||
|
|
9e60f004df | ||
|
|
324f8eb8f4 | ||
|
|
b39200e3ff | ||
|
|
ed8ffde7d0 | ||
|
|
e70c6d961c | ||
|
|
93ad0eeccf | ||
|
|
b5b6b5aca8 | ||
|
|
2c2f9486f9 | ||
|
|
cd60c44ba5 | ||
|
|
09f605e45d | ||
|
|
40ef1f6989 | ||
|
|
eeffa5897d | ||
|
|
d45e129e46 | ||
|
|
d57e3461ff | ||
|
|
1d2d19c9ef | ||
|
|
f3ba84589d | ||
|
|
7cee390b2e | ||
|
|
024ac9eb8c | ||
|
|
d47fe4dcfb | ||
|
|
7d028ce3f5 | ||
|
|
beb819daa4 | ||
|
|
c9d338f159 |
No files matched your search
@@ -12,11 +12,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4.0.1
|
||||
uses: actions/setup-dotnet@v5.1.0
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: 9.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
|
||||
@@ -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<ColorARGB32, byte, ColorBGRA32, 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.13.12" />
|
||||
</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, ColorBGRA32, byte>(inputData, Width, Height, outputData);
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.1.0" />
|
||||
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -82,14 +82,14 @@ internal static class NumericConversionGenerator
|
||||
writer.WriteLine("if (typeof(TFrom) == typeof(TTo))");
|
||||
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)
|
||||
{
|
||||
writer.WriteLine($"else if (typeof(TFrom) == typeof({from.LangName}))");
|
||||
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");
|
||||
@@ -133,40 +133,41 @@ internal static class NumericConversionGenerator
|
||||
{
|
||||
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))
|
||||
{
|
||||
using (new If(writer, "IntPtr.Size == sizeof(int)"))
|
||||
{
|
||||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (to.IsFloatingPoint)
|
||||
{
|
||||
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
|
||||
{
|
||||
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.WriteLine($"return {ConvertMethodName(typeof(float))}<TTo>((float)value);");
|
||||
writer.WriteComment($"We use {conversionType.LangName} because it has enough precision to convert from {from.LangName} to {to.LangName}.");
|
||||
writer.WriteLine($"return {ConvertMethodName(conversionType.Type)}<TTo>(({conversionType.LangName})value);");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -175,7 +176,7 @@ internal static class NumericConversionGenerator
|
||||
string maxValue = to.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($"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);
|
||||
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.WriteLine($"float x = {methodName}<float>(value);");
|
||||
writer.WriteLine("Half converted = (Half)x;");
|
||||
writer.WriteLine("return Unsafe.As<Half, TTo>(ref converted);");
|
||||
writer.WriteComment($"There isn't enough precision to convert from {from.LangName} to {to.LangName}, so we convert to {conversionType.LangName} first.");
|
||||
writer.WriteLine($"{conversionType.LangName} x = {methodName}<{conversionType.LangName}>(value);");
|
||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName})x;");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||
}
|
||||
else
|
||||
{
|
||||
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
|
||||
@@ -214,7 +216,7 @@ internal static class NumericConversionGenerator
|
||||
writer.Write($"(({conversionType})value << {i * from.Size * 8}) | ");
|
||||
}
|
||||
writer.WriteLine("value);");
|
||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -225,7 +227,14 @@ internal static class NumericConversionGenerator
|
||||
writer.WriteComment("This is a special case where we already know an optimal algorithm.");
|
||||
writer.WriteLine("uint x = (value * 255u + 32895u) >> 16;");
|
||||
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
|
||||
{
|
||||
@@ -236,7 +245,7 @@ internal static class NumericConversionGenerator
|
||||
{
|
||||
int offset = (from.Size / to.Size - 1) * to.Size * 8;
|
||||
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))
|
||||
{
|
||||
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");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using AssetRipper.Text.SourceGeneration;
|
||||
using AssetRipper.TextureDecoder.SourceGeneration.Common;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.ColorGenerator;
|
||||
@@ -12,38 +13,41 @@ internal static partial class Program
|
||||
private const string FormatsNamespace = "AssetRipper.TextureDecoder.Rgb.Formats";
|
||||
private const string FormatsFolder = "../../../../AssetRipper.TextureDecoder/Rgb/Formats/";
|
||||
|
||||
/// <summary>
|
||||
/// Name, Type, Red, Blue, Green, Alpha, Fully Utilized
|
||||
/// </summary>
|
||||
private static readonly List<(string, Type, bool, bool, bool, bool, bool)> CustomColors = new()
|
||||
private readonly record struct CustomColorDetails(string Name, Type Type, bool HasRed, bool HasGreen, bool HasBlue, bool HasAlpha, bool FullyUtilized)
|
||||
{
|
||||
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 ),
|
||||
( "ColorARGB32", typeof(byte), true, true, true, true, true ),
|
||||
( "ColorBGRA32", typeof(byte), true, true, true, true, true ),
|
||||
( "ColorRGB16", typeof(byte), true, true, true, false, false ),
|
||||
( "ColorRGB9e5", typeof(double), true, true, true, false, false ),
|
||||
( "ColorRGBA16", typeof(byte), true, true, true, true, false ),
|
||||
};
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Name, Red, Blue, Green, Alpha
|
||||
/// </summary>
|
||||
private static readonly List<(string, bool, bool, bool, bool)> GenericColors = new()
|
||||
{
|
||||
( "ColorR", true, false, false, false ),
|
||||
( "ColorRG", true, true, false, false ),
|
||||
( "ColorRGB", true, true, true, false ),
|
||||
( "ColorRGBA", true, true, true, true ),
|
||||
( "ColorA", false, false, false, true ),
|
||||
};
|
||||
private static readonly string[] GenericColors =
|
||||
[
|
||||
"ColorR",
|
||||
"ColorRG",
|
||||
"ColorRGB",
|
||||
"ColorRGBA",
|
||||
"ColorA",
|
||||
"ColorARGB",
|
||||
"ColorBGRA",
|
||||
"ColorBGR",
|
||||
];
|
||||
|
||||
static void Main()
|
||||
{
|
||||
foreach (var customColor in CustomColors)
|
||||
foreach (CustomColorDetails customColor in CustomColors)
|
||||
{
|
||||
WriteCustomColor(customColor);
|
||||
}
|
||||
foreach (var genericColor in GenericColors)
|
||||
foreach (string genericColor in GenericColors)
|
||||
{
|
||||
WriteGenericColor(genericColor);
|
||||
}
|
||||
@@ -55,7 +59,7 @@ internal static partial class Program
|
||||
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;
|
||||
Console.WriteLine(name);
|
||||
@@ -63,12 +67,11 @@ internal static partial class Program
|
||||
WriteCustomColor(writer, name, type, hasRed, hasGreen, hasBlue, hasAlpha, fullyUtilized);
|
||||
}
|
||||
|
||||
private static void WriteGenericColor((string, bool, bool, bool, bool) details)
|
||||
private static void WriteGenericColor(string name)
|
||||
{
|
||||
(string name, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha) = details;
|
||||
Console.WriteLine(name);
|
||||
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(FormatsFolder, name);
|
||||
WriteGenericColor(writer, name, hasRed, hasGreen, hasBlue, hasAlpha);
|
||||
WriteGenericColor(writer, name);
|
||||
}
|
||||
|
||||
private static void WriteSuperGenericColor(int channelCount)
|
||||
@@ -214,7 +217,7 @@ internal static partial class Program
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteGenericColor(IndentedTextWriter writer, string name, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha)
|
||||
private static void WriteGenericColor(IndentedTextWriter writer, string name)
|
||||
{
|
||||
const string typeName = "T";
|
||||
const string minValue = "NumericConversion.GetMinimumValueSafe<T>()";
|
||||
@@ -229,14 +232,39 @@ internal static partial class Program
|
||||
writer.WriteLine($"public partial struct {name}<T> : IColor<{name}<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
WriteProperty(writer, hasRed, typeName, 'R', minValue);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteProperty(writer, hasGreen, typeName, 'G', minValue);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteProperty(writer, hasBlue, typeName, 'B', minValue);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteProperty(writer, hasAlpha, typeName, 'A', maxValue);
|
||||
writer.WriteLineNoTabs();
|
||||
Debug.Assert(name.StartsWith("Color", StringComparison.Ordinal));
|
||||
ReadOnlySpan<char> channels = name.AsSpan()[5..];
|
||||
foreach (char channel in channels)
|
||||
{
|
||||
WriteProperty(writer, true, typeName, channel, minValue);
|
||||
writer.WriteLineNoTabs();
|
||||
}
|
||||
|
||||
bool hasRed = channels.Contains('R');
|
||||
bool hasGreen = channels.Contains('G');
|
||||
bool hasBlue = channels.Contains('B');
|
||||
bool hasAlpha = channels.Contains('A');
|
||||
|
||||
if (!hasRed)
|
||||
{
|
||||
WriteProperty(writer, hasRed, typeName, 'R', minValue);
|
||||
writer.WriteLineNoTabs();
|
||||
}
|
||||
if (!hasGreen)
|
||||
{
|
||||
WriteProperty(writer, hasGreen, typeName, 'G', minValue);
|
||||
writer.WriteLineNoTabs();
|
||||
}
|
||||
if (!hasBlue)
|
||||
{
|
||||
WriteProperty(writer, hasBlue, typeName, 'B', minValue);
|
||||
writer.WriteLineNoTabs();
|
||||
}
|
||||
if (!hasAlpha)
|
||||
{
|
||||
WriteProperty(writer, hasAlpha, typeName, 'A', maxValue);
|
||||
writer.WriteLineNoTabs();
|
||||
}
|
||||
|
||||
WriteConstructor(writer, name, hasRed, hasGreen, hasBlue, hasAlpha, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
@@ -27,7 +27,7 @@ internal static partial class Program
|
||||
string args6 = args.GetArgument(6);
|
||||
byte[] data = File.ReadAllBytes(path);
|
||||
|
||||
DirectBitmap<ColorBGRA32, byte> bitmap = new DirectBitmap<ColorBGRA32, byte>(width, height);
|
||||
DirectBitmap<ColorBGRA<byte>, byte> bitmap = new DirectBitmap<ColorBGRA<byte>, byte>(width, height);
|
||||
switch (inputType)
|
||||
{
|
||||
case "astc":
|
||||
@@ -52,43 +52,40 @@ internal static partial class Program
|
||||
DecodeRgb(data, width, height, args5, bitmap.Bits);
|
||||
break;
|
||||
case "yuy2":
|
||||
Yuy2Decoder.DecompressYUY2(data, width, height, bitmap.Bits);
|
||||
Yuy2Decoder.DecompressYUY2<ColorBGRA<byte>, byte>(data, width, height, bitmap.Bits);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(inputType);
|
||||
}
|
||||
|
||||
string dirPath = Path.GetDirectoryName(path) ?? Environment.CurrentDirectory;
|
||||
string name = Path.GetFileName(path);
|
||||
|
||||
switch (outputType)
|
||||
{
|
||||
case "bgra":
|
||||
WriteAllBytes(Path.Combine(dirPath, name + ".bgra"), bitmap.Bits);
|
||||
File.WriteAllBytes(path + ".bgra", bitmap.Bits);
|
||||
break;
|
||||
case "bmp":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsBmp(Path.Combine(dirPath, name + ".bmp"));
|
||||
bitmap.SaveAsBmp(path + ".bmp");
|
||||
break;
|
||||
case "exr":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsExr(Path.Combine(dirPath, name + ".exr"));
|
||||
bitmap.SaveAsExr(path + ".exr");
|
||||
break;
|
||||
case "hdr":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsHdr(Path.Combine(dirPath, name + ".hdr"));
|
||||
bitmap.SaveAsHdr(path + ".hdr");
|
||||
break;
|
||||
case "jpg":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsJpg(Path.Combine(dirPath, name + ".jpg"));
|
||||
bitmap.SaveAsJpg(path + ".jpg");
|
||||
break;
|
||||
case "png":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsPng(Path.Combine(dirPath, name + ".png"));
|
||||
bitmap.SaveAsPng(path + ".png");
|
||||
break;
|
||||
case "tga":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsTga(Path.Combine(dirPath, name + ".tga"));
|
||||
bitmap.SaveAsTga(path + ".tga");
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(outputType);
|
||||
@@ -108,7 +105,7 @@ internal static partial class Program
|
||||
Console.WriteLine("Arg at index 6 : blockYSize");
|
||||
int blockXSize = int.Parse(blockXSizeString);
|
||||
int blockYSize = int.Parse(blockYSizeString);
|
||||
AstcDecoder.DecodeASTC(input, width, height, blockXSize, blockYSize, output);
|
||||
AstcDecoder.DecodeASTC<ColorBGRA<byte>, byte>(input, width, height, blockXSize, blockYSize, output);
|
||||
}
|
||||
|
||||
private static void DecodeAtc(ReadOnlySpan<byte> input, int width, int height, string modeString, Span<byte> output)
|
||||
@@ -120,10 +117,10 @@ internal static partial class Program
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
AtcDecoder.DecompressAtcRgb4(input, width, height, output);
|
||||
AtcDecoder.DecompressAtcRgb4<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 1:
|
||||
AtcDecoder.DecompressAtcRgba8(input, width, height, output);
|
||||
AtcDecoder.DecompressAtcRgba8<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -146,25 +143,25 @@ internal static partial class Program
|
||||
switch (mode)
|
||||
{
|
||||
case 1:
|
||||
Bc1.Decompress(input, width, height, output);
|
||||
Bc1.Decompress<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 2:
|
||||
Bc2.Decompress(input, width, height, output);
|
||||
Bc2.Decompress<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 3:
|
||||
Bc3.Decompress(input, width, height, output);
|
||||
Bc3.Decompress<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 4:
|
||||
Bc4.Decompress(input, width, height, output);
|
||||
Bc4.Decompress<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 5:
|
||||
Bc5.Decompress(input, width, height, output);
|
||||
Bc5.Decompress<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 6:
|
||||
Bc6h.Decompress(input, width, height, bool.Parse(isSignedString), output);
|
||||
Bc6h.Decompress<ColorBGRA<byte>, byte>(input, width, height, bool.Parse(isSignedString), output);
|
||||
break;
|
||||
case 7:
|
||||
Bc7.Decompress(input, width, height, output);
|
||||
Bc7.Decompress<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -182,13 +179,13 @@ internal static partial class Program
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
DxtDecoder.DecompressDXT1(input, width, height, output);
|
||||
DxtDecoder.DecompressDXT1<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 1:
|
||||
DxtDecoder.DecompressDXT3(input, width, height, output);
|
||||
DxtDecoder.DecompressDXT3<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 2:
|
||||
DxtDecoder.DecompressDXT5(input, width, height, output);
|
||||
DxtDecoder.DecompressDXT5<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -211,28 +208,28 @@ internal static partial class Program
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
EtcDecoder.DecompressETC(input, width, height, output);
|
||||
EtcDecoder.DecompressETC<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 1:
|
||||
EtcDecoder.DecompressETC2(input, width, height, output);
|
||||
EtcDecoder.DecompressETC2<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 2:
|
||||
EtcDecoder.DecompressETC2A1(input, width, height, output);
|
||||
EtcDecoder.DecompressETC2A1<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 3:
|
||||
EtcDecoder.DecompressETC2A8(input, width, height, output);
|
||||
EtcDecoder.DecompressETC2A8<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 4:
|
||||
EtcDecoder.DecompressEACRUnsigned(input, width, height, output);
|
||||
EtcDecoder.DecompressEACRUnsigned<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 5:
|
||||
EtcDecoder.DecompressEACRSigned(input, width, height, output);
|
||||
EtcDecoder.DecompressEACRSigned<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 6:
|
||||
EtcDecoder.DecompressEACRGUnsigned(input, width, height, output);
|
||||
EtcDecoder.DecompressEACRGUnsigned<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 7:
|
||||
EtcDecoder.DecompressEACRGSigned(input, width, height, output);
|
||||
EtcDecoder.DecompressEACRGSigned<ColorBGRA<byte>, byte>(input, width, height, output);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -244,13 +241,6 @@ internal static partial class Program
|
||||
{
|
||||
Console.WriteLine("Arg at index 5 : 2bitMode");
|
||||
bool do2bit = bool.Parse(do2bitModeString);
|
||||
PvrtcDecoder.DecompressPVRTC(input, width, height, do2bit, output);
|
||||
}
|
||||
|
||||
public static void WriteAllBytes(string path, ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrEmpty(path, nameof(path));
|
||||
using Microsoft.Win32.SafeHandles.SafeFileHandle sfh = File.OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||
RandomAccess.Write(sfh, bytes, 0);
|
||||
PvrtcDecoder.DecompressPVRTC<ColorBGRA<byte>, byte>(input, width, height, do2bit, output);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -15,367 +16,495 @@ static partial class Program
|
||||
Console.WriteLine(" 2 - RGB_SByte");
|
||||
Console.WriteLine(" 3 - RGBA_SByte");
|
||||
Console.WriteLine(" 4 - A_SByte");
|
||||
Console.WriteLine(" 5 - R_Byte");
|
||||
Console.WriteLine(" 6 - RG_Byte");
|
||||
Console.WriteLine(" 7 - RGB_Byte");
|
||||
Console.WriteLine(" 8 - RGBA_Byte");
|
||||
Console.WriteLine(" 9 - A_Byte");
|
||||
Console.WriteLine(" 10 - R_Int16");
|
||||
Console.WriteLine(" 11 - RG_Int16");
|
||||
Console.WriteLine(" 12 - RGB_Int16");
|
||||
Console.WriteLine(" 13 - RGBA_Int16");
|
||||
Console.WriteLine(" 14 - A_Int16");
|
||||
Console.WriteLine(" 15 - R_UInt16");
|
||||
Console.WriteLine(" 16 - RG_UInt16");
|
||||
Console.WriteLine(" 17 - RGB_UInt16");
|
||||
Console.WriteLine(" 18 - RGBA_UInt16");
|
||||
Console.WriteLine(" 19 - A_UInt16");
|
||||
Console.WriteLine(" 20 - R_Int32");
|
||||
Console.WriteLine(" 21 - RG_Int32");
|
||||
Console.WriteLine(" 22 - RGB_Int32");
|
||||
Console.WriteLine(" 23 - RGBA_Int32");
|
||||
Console.WriteLine(" 24 - A_Int32");
|
||||
Console.WriteLine(" 25 - R_UInt32");
|
||||
Console.WriteLine(" 26 - RG_UInt32");
|
||||
Console.WriteLine(" 27 - RGB_UInt32");
|
||||
Console.WriteLine(" 28 - RGBA_UInt32");
|
||||
Console.WriteLine(" 29 - A_UInt32");
|
||||
Console.WriteLine(" 30 - R_IntPtr");
|
||||
Console.WriteLine(" 31 - RG_IntPtr");
|
||||
Console.WriteLine(" 32 - RGB_IntPtr");
|
||||
Console.WriteLine(" 33 - RGBA_IntPtr");
|
||||
Console.WriteLine(" 34 - A_IntPtr");
|
||||
Console.WriteLine(" 35 - R_UIntPtr");
|
||||
Console.WriteLine(" 36 - RG_UIntPtr");
|
||||
Console.WriteLine(" 37 - RGB_UIntPtr");
|
||||
Console.WriteLine(" 38 - RGBA_UIntPtr");
|
||||
Console.WriteLine(" 39 - A_UIntPtr");
|
||||
Console.WriteLine(" 40 - R_Int64");
|
||||
Console.WriteLine(" 41 - RG_Int64");
|
||||
Console.WriteLine(" 42 - RGB_Int64");
|
||||
Console.WriteLine(" 43 - RGBA_Int64");
|
||||
Console.WriteLine(" 44 - A_Int64");
|
||||
Console.WriteLine(" 45 - R_UInt64");
|
||||
Console.WriteLine(" 46 - RG_UInt64");
|
||||
Console.WriteLine(" 47 - RGB_UInt64");
|
||||
Console.WriteLine(" 48 - RGBA_UInt64");
|
||||
Console.WriteLine(" 49 - A_UInt64");
|
||||
Console.WriteLine(" 50 - R_Int128");
|
||||
Console.WriteLine(" 51 - RG_Int128");
|
||||
Console.WriteLine(" 52 - RGB_Int128");
|
||||
Console.WriteLine(" 53 - RGBA_Int128");
|
||||
Console.WriteLine(" 54 - A_Int128");
|
||||
Console.WriteLine(" 55 - R_UInt128");
|
||||
Console.WriteLine(" 56 - RG_UInt128");
|
||||
Console.WriteLine(" 57 - RGB_UInt128");
|
||||
Console.WriteLine(" 58 - RGBA_UInt128");
|
||||
Console.WriteLine(" 59 - A_UInt128");
|
||||
Console.WriteLine(" 60 - R_Half");
|
||||
Console.WriteLine(" 61 - RG_Half");
|
||||
Console.WriteLine(" 62 - RGB_Half");
|
||||
Console.WriteLine(" 63 - RGBA_Half");
|
||||
Console.WriteLine(" 64 - A_Half");
|
||||
Console.WriteLine(" 65 - R_Single");
|
||||
Console.WriteLine(" 66 - RG_Single");
|
||||
Console.WriteLine(" 67 - RGB_Single");
|
||||
Console.WriteLine(" 68 - RGBA_Single");
|
||||
Console.WriteLine(" 69 - A_Single");
|
||||
Console.WriteLine(" 70 - R_NFloat");
|
||||
Console.WriteLine(" 71 - RG_NFloat");
|
||||
Console.WriteLine(" 72 - RGB_NFloat");
|
||||
Console.WriteLine(" 73 - RGBA_NFloat");
|
||||
Console.WriteLine(" 74 - A_NFloat");
|
||||
Console.WriteLine(" 75 - R_Double");
|
||||
Console.WriteLine(" 76 - RG_Double");
|
||||
Console.WriteLine(" 77 - RGB_Double");
|
||||
Console.WriteLine(" 78 - RGBA_Double");
|
||||
Console.WriteLine(" 79 - A_Double");
|
||||
Console.WriteLine(" 80 - R_Decimal");
|
||||
Console.WriteLine(" 81 - RG_Decimal");
|
||||
Console.WriteLine(" 82 - RGB_Decimal");
|
||||
Console.WriteLine(" 83 - RGBA_Decimal");
|
||||
Console.WriteLine(" 84 - A_Decimal");
|
||||
Console.WriteLine(" 85 - ARGB16");
|
||||
Console.WriteLine(" 86 - ARGB32");
|
||||
Console.WriteLine(" 87 - BGRA32");
|
||||
Console.WriteLine(" 88 - RGB16");
|
||||
Console.WriteLine(" 89 - RGB9e5");
|
||||
Console.WriteLine(" 90 - RGBA16");
|
||||
Console.WriteLine(" 5 - ARGB_SByte");
|
||||
Console.WriteLine(" 6 - BGRA_SByte");
|
||||
Console.WriteLine(" 7 - R_Byte");
|
||||
Console.WriteLine(" 8 - RG_Byte");
|
||||
Console.WriteLine(" 9 - RGB_Byte");
|
||||
Console.WriteLine(" 10 - RGBA_Byte");
|
||||
Console.WriteLine(" 11 - A_Byte");
|
||||
Console.WriteLine(" 12 - ARGB_Byte");
|
||||
Console.WriteLine(" 13 - BGRA_Byte");
|
||||
Console.WriteLine(" 14 - R_Int16");
|
||||
Console.WriteLine(" 15 - RG_Int16");
|
||||
Console.WriteLine(" 16 - RGB_Int16");
|
||||
Console.WriteLine(" 17 - RGBA_Int16");
|
||||
Console.WriteLine(" 18 - A_Int16");
|
||||
Console.WriteLine(" 19 - ARGB_Int16");
|
||||
Console.WriteLine(" 20 - BGRA_Int16");
|
||||
Console.WriteLine(" 21 - R_UInt16");
|
||||
Console.WriteLine(" 22 - RG_UInt16");
|
||||
Console.WriteLine(" 23 - RGB_UInt16");
|
||||
Console.WriteLine(" 24 - RGBA_UInt16");
|
||||
Console.WriteLine(" 25 - A_UInt16");
|
||||
Console.WriteLine(" 26 - ARGB_UInt16");
|
||||
Console.WriteLine(" 27 - BGRA_UInt16");
|
||||
Console.WriteLine(" 28 - R_Int32");
|
||||
Console.WriteLine(" 29 - RG_Int32");
|
||||
Console.WriteLine(" 30 - RGB_Int32");
|
||||
Console.WriteLine(" 31 - RGBA_Int32");
|
||||
Console.WriteLine(" 32 - A_Int32");
|
||||
Console.WriteLine(" 33 - ARGB_Int32");
|
||||
Console.WriteLine(" 34 - BGRA_Int32");
|
||||
Console.WriteLine(" 35 - R_UInt32");
|
||||
Console.WriteLine(" 36 - RG_UInt32");
|
||||
Console.WriteLine(" 37 - RGB_UInt32");
|
||||
Console.WriteLine(" 38 - RGBA_UInt32");
|
||||
Console.WriteLine(" 39 - A_UInt32");
|
||||
Console.WriteLine(" 40 - ARGB_UInt32");
|
||||
Console.WriteLine(" 41 - BGRA_UInt32");
|
||||
Console.WriteLine(" 42 - R_IntPtr");
|
||||
Console.WriteLine(" 43 - RG_IntPtr");
|
||||
Console.WriteLine(" 44 - RGB_IntPtr");
|
||||
Console.WriteLine(" 45 - RGBA_IntPtr");
|
||||
Console.WriteLine(" 46 - A_IntPtr");
|
||||
Console.WriteLine(" 47 - ARGB_IntPtr");
|
||||
Console.WriteLine(" 48 - BGRA_IntPtr");
|
||||
Console.WriteLine(" 49 - R_UIntPtr");
|
||||
Console.WriteLine(" 50 - RG_UIntPtr");
|
||||
Console.WriteLine(" 51 - RGB_UIntPtr");
|
||||
Console.WriteLine(" 52 - RGBA_UIntPtr");
|
||||
Console.WriteLine(" 53 - A_UIntPtr");
|
||||
Console.WriteLine(" 54 - ARGB_UIntPtr");
|
||||
Console.WriteLine(" 55 - BGRA_UIntPtr");
|
||||
Console.WriteLine(" 56 - R_Int64");
|
||||
Console.WriteLine(" 57 - RG_Int64");
|
||||
Console.WriteLine(" 58 - RGB_Int64");
|
||||
Console.WriteLine(" 59 - RGBA_Int64");
|
||||
Console.WriteLine(" 60 - A_Int64");
|
||||
Console.WriteLine(" 61 - ARGB_Int64");
|
||||
Console.WriteLine(" 62 - BGRA_Int64");
|
||||
Console.WriteLine(" 63 - R_UInt64");
|
||||
Console.WriteLine(" 64 - RG_UInt64");
|
||||
Console.WriteLine(" 65 - RGB_UInt64");
|
||||
Console.WriteLine(" 66 - RGBA_UInt64");
|
||||
Console.WriteLine(" 67 - A_UInt64");
|
||||
Console.WriteLine(" 68 - ARGB_UInt64");
|
||||
Console.WriteLine(" 69 - BGRA_UInt64");
|
||||
Console.WriteLine(" 70 - R_Int128");
|
||||
Console.WriteLine(" 71 - RG_Int128");
|
||||
Console.WriteLine(" 72 - RGB_Int128");
|
||||
Console.WriteLine(" 73 - RGBA_Int128");
|
||||
Console.WriteLine(" 74 - A_Int128");
|
||||
Console.WriteLine(" 75 - ARGB_Int128");
|
||||
Console.WriteLine(" 76 - BGRA_Int128");
|
||||
Console.WriteLine(" 77 - R_UInt128");
|
||||
Console.WriteLine(" 78 - RG_UInt128");
|
||||
Console.WriteLine(" 79 - RGB_UInt128");
|
||||
Console.WriteLine(" 80 - RGBA_UInt128");
|
||||
Console.WriteLine(" 81 - A_UInt128");
|
||||
Console.WriteLine(" 82 - ARGB_UInt128");
|
||||
Console.WriteLine(" 83 - BGRA_UInt128");
|
||||
Console.WriteLine(" 84 - R_Half");
|
||||
Console.WriteLine(" 85 - RG_Half");
|
||||
Console.WriteLine(" 86 - RGB_Half");
|
||||
Console.WriteLine(" 87 - RGBA_Half");
|
||||
Console.WriteLine(" 88 - A_Half");
|
||||
Console.WriteLine(" 89 - ARGB_Half");
|
||||
Console.WriteLine(" 90 - BGRA_Half");
|
||||
Console.WriteLine(" 91 - R_Single");
|
||||
Console.WriteLine(" 92 - RG_Single");
|
||||
Console.WriteLine(" 93 - RGB_Single");
|
||||
Console.WriteLine(" 94 - RGBA_Single");
|
||||
Console.WriteLine(" 95 - A_Single");
|
||||
Console.WriteLine(" 96 - ARGB_Single");
|
||||
Console.WriteLine(" 97 - BGRA_Single");
|
||||
Console.WriteLine(" 98 - R_NFloat");
|
||||
Console.WriteLine(" 99 - RG_NFloat");
|
||||
Console.WriteLine(" 100 - RGB_NFloat");
|
||||
Console.WriteLine(" 101 - RGBA_NFloat");
|
||||
Console.WriteLine(" 102 - A_NFloat");
|
||||
Console.WriteLine(" 103 - ARGB_NFloat");
|
||||
Console.WriteLine(" 104 - BGRA_NFloat");
|
||||
Console.WriteLine(" 105 - R_Double");
|
||||
Console.WriteLine(" 106 - RG_Double");
|
||||
Console.WriteLine(" 107 - RGB_Double");
|
||||
Console.WriteLine(" 108 - RGBA_Double");
|
||||
Console.WriteLine(" 109 - A_Double");
|
||||
Console.WriteLine(" 110 - ARGB_Double");
|
||||
Console.WriteLine(" 111 - BGRA_Double");
|
||||
Console.WriteLine(" 112 - R_Decimal");
|
||||
Console.WriteLine(" 113 - RG_Decimal");
|
||||
Console.WriteLine(" 114 - RGB_Decimal");
|
||||
Console.WriteLine(" 115 - RGBA_Decimal");
|
||||
Console.WriteLine(" 116 - A_Decimal");
|
||||
Console.WriteLine(" 117 - ARGB_Decimal");
|
||||
Console.WriteLine(" 118 - BGRA_Decimal");
|
||||
Console.WriteLine(" 119 - ARGB16");
|
||||
Console.WriteLine(" 120 - RGB16");
|
||||
Console.WriteLine(" 121 - RGB9e5");
|
||||
Console.WriteLine(" 122 - RGBA16");
|
||||
int mode = int.Parse(modeString);
|
||||
switch(mode)
|
||||
{
|
||||
case 0:
|
||||
RgbConverter.Convert<ColorR<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 1:
|
||||
RgbConverter.Convert<ColorRG<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 2:
|
||||
RgbConverter.Convert<ColorRGB<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 3:
|
||||
RgbConverter.Convert<ColorRGBA<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 4:
|
||||
RgbConverter.Convert<ColorA<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 5:
|
||||
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 6:
|
||||
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 7:
|
||||
RgbConverter.Convert<ColorRGB<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 8:
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 9:
|
||||
RgbConverter.Convert<ColorA<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 10:
|
||||
RgbConverter.Convert<ColorR<short>, short, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 11:
|
||||
RgbConverter.Convert<ColorRG<short>, short, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 12:
|
||||
RgbConverter.Convert<ColorRGB<short>, short, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 13:
|
||||
RgbConverter.Convert<ColorRGBA<short>, short, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 14:
|
||||
RgbConverter.Convert<ColorA<short>, short, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 15:
|
||||
RgbConverter.Convert<ColorR<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 16:
|
||||
RgbConverter.Convert<ColorRG<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 17:
|
||||
RgbConverter.Convert<ColorRGB<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 18:
|
||||
RgbConverter.Convert<ColorRGBA<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 19:
|
||||
RgbConverter.Convert<ColorA<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 20:
|
||||
RgbConverter.Convert<ColorR<int>, int, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 21:
|
||||
RgbConverter.Convert<ColorRG<int>, int, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 22:
|
||||
RgbConverter.Convert<ColorRGB<int>, int, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 23:
|
||||
RgbConverter.Convert<ColorRGBA<int>, int, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 24:
|
||||
RgbConverter.Convert<ColorA<int>, int, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 25:
|
||||
RgbConverter.Convert<ColorR<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 26:
|
||||
RgbConverter.Convert<ColorRG<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 27:
|
||||
RgbConverter.Convert<ColorRGB<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 28:
|
||||
RgbConverter.Convert<ColorRGBA<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 29:
|
||||
RgbConverter.Convert<ColorA<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 30:
|
||||
RgbConverter.Convert<ColorR<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 31:
|
||||
RgbConverter.Convert<ColorRG<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 32:
|
||||
RgbConverter.Convert<ColorRGB<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 33:
|
||||
RgbConverter.Convert<ColorRGBA<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 34:
|
||||
RgbConverter.Convert<ColorA<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 35:
|
||||
RgbConverter.Convert<ColorR<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 36:
|
||||
RgbConverter.Convert<ColorRG<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 37:
|
||||
RgbConverter.Convert<ColorRGB<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 38:
|
||||
RgbConverter.Convert<ColorRGBA<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 39:
|
||||
RgbConverter.Convert<ColorA<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 40:
|
||||
RgbConverter.Convert<ColorR<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 41:
|
||||
RgbConverter.Convert<ColorRG<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 42:
|
||||
RgbConverter.Convert<ColorRGB<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 43:
|
||||
RgbConverter.Convert<ColorRGBA<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 44:
|
||||
RgbConverter.Convert<ColorA<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 45:
|
||||
RgbConverter.Convert<ColorR<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 46:
|
||||
RgbConverter.Convert<ColorRG<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 47:
|
||||
RgbConverter.Convert<ColorRGB<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 48:
|
||||
RgbConverter.Convert<ColorRGBA<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 49:
|
||||
RgbConverter.Convert<ColorA<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 50:
|
||||
RgbConverter.Convert<ColorR<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 51:
|
||||
RgbConverter.Convert<ColorRG<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 52:
|
||||
RgbConverter.Convert<ColorRGB<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 53:
|
||||
RgbConverter.Convert<ColorRGBA<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 54:
|
||||
RgbConverter.Convert<ColorA<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 55:
|
||||
RgbConverter.Convert<ColorR<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 56:
|
||||
RgbConverter.Convert<ColorRG<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 57:
|
||||
RgbConverter.Convert<ColorRGB<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 58:
|
||||
RgbConverter.Convert<ColorRGBA<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 59:
|
||||
RgbConverter.Convert<ColorA<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 60:
|
||||
RgbConverter.Convert<ColorR<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 61:
|
||||
RgbConverter.Convert<ColorRG<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 62:
|
||||
RgbConverter.Convert<ColorRGB<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 63:
|
||||
RgbConverter.Convert<ColorRGBA<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 64:
|
||||
RgbConverter.Convert<ColorA<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 65:
|
||||
RgbConverter.Convert<ColorR<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 66:
|
||||
RgbConverter.Convert<ColorRG<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 67:
|
||||
RgbConverter.Convert<ColorRGB<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 68:
|
||||
RgbConverter.Convert<ColorRGBA<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 69:
|
||||
RgbConverter.Convert<ColorA<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 70:
|
||||
RgbConverter.Convert<ColorR<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 71:
|
||||
RgbConverter.Convert<ColorRG<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 72:
|
||||
RgbConverter.Convert<ColorRGB<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 73:
|
||||
RgbConverter.Convert<ColorRGBA<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 74:
|
||||
RgbConverter.Convert<ColorA<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 75:
|
||||
RgbConverter.Convert<ColorR<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 76:
|
||||
RgbConverter.Convert<ColorRG<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 77:
|
||||
RgbConverter.Convert<ColorRGB<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 78:
|
||||
RgbConverter.Convert<ColorRGBA<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 79:
|
||||
RgbConverter.Convert<ColorA<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 80:
|
||||
RgbConverter.Convert<ColorR<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 81:
|
||||
RgbConverter.Convert<ColorRG<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 82:
|
||||
RgbConverter.Convert<ColorRGB<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 83:
|
||||
RgbConverter.Convert<ColorRGBA<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 84:
|
||||
RgbConverter.Convert<ColorA<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 85:
|
||||
RgbConverter.Convert<ColorARGB16, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 86:
|
||||
RgbConverter.Convert<ColorARGB32, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 87:
|
||||
RgbConverter.Convert<ColorBGRA32, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 88:
|
||||
RgbConverter.Convert<ColorRGB16, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 89:
|
||||
RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 90:
|
||||
RgbConverter.Convert<ColorRGBA16, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 91:
|
||||
RgbConverter.Convert<ColorR<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 92:
|
||||
RgbConverter.Convert<ColorRG<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 93:
|
||||
RgbConverter.Convert<ColorRGB<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 94:
|
||||
RgbConverter.Convert<ColorRGBA<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 95:
|
||||
RgbConverter.Convert<ColorA<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 96:
|
||||
RgbConverter.Convert<ColorARGB<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 97:
|
||||
RgbConverter.Convert<ColorBGRA<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 98:
|
||||
RgbConverter.Convert<ColorR<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 99:
|
||||
RgbConverter.Convert<ColorRG<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 100:
|
||||
RgbConverter.Convert<ColorRGB<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 101:
|
||||
RgbConverter.Convert<ColorRGBA<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 102:
|
||||
RgbConverter.Convert<ColorA<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 103:
|
||||
RgbConverter.Convert<ColorARGB<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 104:
|
||||
RgbConverter.Convert<ColorBGRA<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 105:
|
||||
RgbConverter.Convert<ColorR<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 106:
|
||||
RgbConverter.Convert<ColorRG<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 107:
|
||||
RgbConverter.Convert<ColorRGB<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 108:
|
||||
RgbConverter.Convert<ColorRGBA<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 109:
|
||||
RgbConverter.Convert<ColorA<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 110:
|
||||
RgbConverter.Convert<ColorARGB<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 111:
|
||||
RgbConverter.Convert<ColorBGRA<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 112:
|
||||
RgbConverter.Convert<ColorR<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 113:
|
||||
RgbConverter.Convert<ColorRG<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 114:
|
||||
RgbConverter.Convert<ColorRGB<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 115:
|
||||
RgbConverter.Convert<ColorRGBA<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 116:
|
||||
RgbConverter.Convert<ColorA<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 117:
|
||||
RgbConverter.Convert<ColorARGB<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 118:
|
||||
RgbConverter.Convert<ColorBGRA<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 119:
|
||||
RgbConverter.Convert<ColorARGB16, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 120:
|
||||
RgbConverter.Convert<ColorRGB16, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 121:
|
||||
RgbConverter.Convert<ColorRGB9e5, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 122:
|
||||
RgbConverter.Convert<ColorRGBA16, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(mode.ToString());
|
||||
|
||||
@@ -68,11 +68,18 @@ public static class CSharpPrimitives
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 Type Type { get; }
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.1.0" />
|
||||
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,5 +1,6 @@
|
||||
using AssetRipper.Text.SourceGeneration;
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using AssetRipper.TextureDecoder.SourceGeneration.Common;
|
||||
using System.CodeDom.Compiler;
|
||||
@@ -12,8 +13,6 @@ namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
internal static readonly List<GenerationData> dataList = new()
|
||||
{
|
||||
GenerationData.Create<ColorARGB16>(2),
|
||||
GenerationData.Create<ColorARGB32>(4),
|
||||
GenerationData.Create<ColorBGRA32>(4),
|
||||
GenerationData.Create<ColorRGB16>(2),
|
||||
GenerationData.Create<ColorRGB9e5>(4),
|
||||
GenerationData.Create<ColorRGBA16>(2),
|
||||
@@ -35,7 +34,8 @@ namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
writer.WriteGeneratedCodeWarning();
|
||||
writer.WriteLine();
|
||||
writer.WriteLine($"using {typeof(RgbConverter).Namespace};");
|
||||
writer.WriteLine($"using {typeof(ColorBGRA32).Namespace};");
|
||||
writer.WriteLine($"using {typeof(B).Namespace};");
|
||||
writer.WriteLine($"using {typeof(ColorRGBA16).Namespace};");
|
||||
writer.WriteLine($"using {typeof(NFloat).Namespace};");
|
||||
writer.WriteLine();
|
||||
|
||||
|
||||
@@ -75,27 +75,27 @@ internal static class TestClassGenerator
|
||||
}
|
||||
|
||||
textWriter.WriteLineNoTabs();
|
||||
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorRGBA<byte>)}<{channelTypeName}>", channelTypeName);
|
||||
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorRGBA<>)}<{channelTypeName}>", channelTypeName);
|
||||
|
||||
if (!data.AlphaChannel)
|
||||
{
|
||||
textWriter.WriteLineNoTabs();
|
||||
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorRGB<byte>)}<{channelTypeName}>", channelTypeName);
|
||||
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorRGB<>)}<{channelTypeName}>", channelTypeName);
|
||||
if (!data.BlueChannel)
|
||||
{
|
||||
textWriter.WriteLineNoTabs();
|
||||
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorRG<byte>)}<{channelTypeName}>", channelTypeName);
|
||||
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorRG<>)}<{channelTypeName}>", channelTypeName);
|
||||
if (!data.GreenChannel)
|
||||
{
|
||||
textWriter.WriteLineNoTabs();
|
||||
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorR<byte>)}<{channelTypeName}>", channelTypeName);
|
||||
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorR<>)}<{channelTypeName}>", channelTypeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!data.RedChannel && !data.GreenChannel && !data.BlueChannel)
|
||||
{
|
||||
textWriter.WriteLineNoTabs();
|
||||
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorA<byte>)}<{channelTypeName}>", channelTypeName);
|
||||
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorA<>)}<{channelTypeName}>", channelTypeName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,11 +135,13 @@ internal static class TestClassGenerator
|
||||
|
||||
internal static IEnumerable<string> GetGenericColorNames()
|
||||
{
|
||||
yield return nameof(ColorR<byte>);
|
||||
yield return nameof(ColorRG<byte>);
|
||||
yield return nameof(ColorRGB<byte>);
|
||||
yield return nameof(ColorRGBA<byte>);
|
||||
yield return nameof(ColorA<byte>);
|
||||
yield return nameof(ColorR<>);
|
||||
yield return nameof(ColorRG<>);
|
||||
yield return nameof(ColorRGB<>);
|
||||
yield return nameof(ColorRGBA<>);
|
||||
yield return nameof(ColorA<>);
|
||||
yield return nameof(ColorARGB<>);
|
||||
yield return nameof(ColorBGRA<>);
|
||||
}
|
||||
|
||||
private static void MakeLosslessColorTests()
|
||||
@@ -218,7 +220,7 @@ internal static class TestClassGenerator
|
||||
|
||||
textWriter.WriteLine("var color = MakeRandomColor();");
|
||||
textWriter.WriteLine("color.GetChannels(out var r, out var g, out var b, out var a);");
|
||||
textWriter.WriteLine("Assert.Multiple(() =>");
|
||||
textWriter.WriteLine("using (Assert.EnterMultipleScope())");
|
||||
textWriter.WriteLine("{");
|
||||
textWriter.Indent += 1;
|
||||
|
||||
@@ -228,7 +230,7 @@ internal static class TestClassGenerator
|
||||
}
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("});");
|
||||
textWriter.WriteLine("};");
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("}");
|
||||
@@ -248,7 +250,7 @@ internal static class TestClassGenerator
|
||||
}
|
||||
textWriter.WriteLine($"color.{channel} = MakeRandomValue();");
|
||||
|
||||
textWriter.WriteLine("Assert.Multiple(() =>");
|
||||
textWriter.WriteLine("using (Assert.EnterMultipleScope())");
|
||||
textWriter.WriteLine("{");
|
||||
textWriter.Indent += 1;
|
||||
|
||||
@@ -258,7 +260,7 @@ internal static class TestClassGenerator
|
||||
}
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("});");
|
||||
textWriter.WriteLine("};");
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("}");
|
||||
@@ -308,7 +310,7 @@ internal static class TestClassGenerator
|
||||
textWriter.WriteLine("var color = MakeRandomColor();");
|
||||
textWriter.WriteLine("color.GetChannels(out var r, out var g, out var b, out var a);");
|
||||
textWriter.WriteLine("color.SetChannels(r, g, b, a);");
|
||||
textWriter.WriteLine("Assert.Multiple(() =>");
|
||||
textWriter.WriteLine("using (Assert.EnterMultipleScope())");
|
||||
textWriter.WriteLine("{");
|
||||
textWriter.Indent += 1;
|
||||
|
||||
@@ -318,7 +320,7 @@ internal static class TestClassGenerator
|
||||
}
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("});");
|
||||
textWriter.WriteLine("};");
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("}");
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageReference Include="NUnit" Version="4.1.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="4.2.0">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
|
||||
<PackageReference Include="NUnit" Version="4.5.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="4.11.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PackageReference Include="coverlet.collector" Version="8.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using AssetRipper.TextureDecoder.Astc;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests;
|
||||
|
||||
@@ -67,7 +68,7 @@ public sealed class AstcTests
|
||||
private static void AssertCorrectDecompression<T>(int blockWidth, int blockHeight, double maxMeanDeviation, double maxStandardDeviation) where T : ITexture
|
||||
{
|
||||
ReadOnlySpan<byte> data = T.Data;
|
||||
int bytesRead = AstcDecoder.DecodeASTC(data, T.Width, T.Height, blockWidth, blockHeight, out byte[] decompressedData);
|
||||
int bytesRead = AstcDecoder.DecodeASTC<ColorBGRA<byte>, byte>(data, T.Width, T.Height, blockWidth, blockHeight, out byte[] decompressedData);
|
||||
if (!T.Mips)
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
@@ -78,6 +79,6 @@ 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")]
|
||||
public void DecodesBlockWithoutThrowing(byte[] block, int blockWidth, int blockHeight)
|
||||
{
|
||||
Assert.DoesNotThrow(() => AstcDecoder.DecodeASTC(block, blockWidth, blockHeight, blockWidth, blockHeight, out _));
|
||||
Assert.DoesNotThrow(() => AstcDecoder.DecodeASTC<ColorBGRA<byte>, byte>(block, blockWidth, blockHeight, blockWidth, blockHeight, out _));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class AtcTests
|
||||
{
|
||||
@@ -6,24 +8,24 @@
|
||||
public void DecompressAtcRgb4Test()
|
||||
{
|
||||
byte[] data = File.ReadAllBytes(TestFileFolders.AtcTestFiles + "test.atc_rgb4");
|
||||
int bytesRead = Atc.AtcDecoder.DecompressAtcRgb4(data, 256, 256, out byte[] decodedData);
|
||||
Assert.Multiple(() =>
|
||||
int bytesRead = Atc.AtcDecoder.DecompressAtcRgb4<ColorBGRA<byte>, byte>(data, 256, 256, out byte[] decodedData);
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Is.EqualTo(File.ReadAllBytes(TestFileFolders.AtcTestFiles + "test.atc_rgb4_decoded")));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressAtcRgba8Test()
|
||||
{
|
||||
byte[] data = File.ReadAllBytes(TestFileFolders.AtcTestFiles + "test.atc_rgba8");
|
||||
int bytesRead = Atc.AtcDecoder.DecompressAtcRgba8(data, 256, 256, out byte[] decodedData);
|
||||
Assert.Multiple(() =>
|
||||
int bytesRead = Atc.AtcDecoder.DecompressAtcRgba8<ColorBGRA<byte>, byte>(data, 256, 256, out byte[] decodedData);
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Is.EqualTo(File.ReadAllBytes(TestFileFolders.AtcTestFiles + "test.atc_rgba8_decoded")));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,14 +21,14 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressDXT1Test()
|
||||
public void Decompress_BC1()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt1");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4 }) //mipmaps 2 and 1 cause a buffer overrun for the output
|
||||
{
|
||||
int bytesRead = Bc1.Decompress(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT1(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
int bytesRead = Bc1.Decompress<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT1<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
AssertAlmostEqual(dxtDecodedData, bcDecodedData);
|
||||
}
|
||||
@@ -36,14 +36,14 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressDXT3Test()
|
||||
public void Decompress_BC2()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt3");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4 }) //mipmaps 2 and 1 cause a buffer overrun for the output
|
||||
{
|
||||
int bytesRead = Bc2.Decompress(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT3(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
int bytesRead = Bc2.Decompress<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT3<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
AssertAlmostEqual(dxtDecodedData, bcDecodedData);
|
||||
}
|
||||
@@ -51,14 +51,14 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressDXT5Test()
|
||||
public void Decompress_BC3()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt5");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4 }) //mipmaps 2 and 1 cause a buffer overrun for the output
|
||||
{
|
||||
int bytesRead = Bc3.Decompress(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT5(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
int bytesRead = Bc3.Decompress<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT5<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
AssertAlmostEqual(dxtDecodedData, bcDecodedData);
|
||||
}
|
||||
@@ -66,25 +66,25 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC4Test()
|
||||
public void Decompress_BC4()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc4");
|
||||
int bytesRead = Bc4.Decompress(data, 512, 512, out _);
|
||||
int bytesRead = Bc4.Decompress<ColorBGRA<byte>, byte>(data, 512, 512, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC5Test()
|
||||
public void Decompress_BC5()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc5");
|
||||
int bytesRead = Bc5.Decompress(data, 512, 512, out _);
|
||||
int bytesRead = Bc5.Decompress<ColorBGRA<byte>, byte>(data, 512, 512, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[TestCase("test.bc6h_fast")]
|
||||
[TestCase("test.bc6h_normal")]
|
||||
[TestCase("test.bc6h_best")]
|
||||
public void DecompressBC6HTest(string fileName)
|
||||
public void Decompress_BC6H(string fileName)
|
||||
{
|
||||
AssertCorrectBC6HDecompression(TestFileFolders.BcTestFiles + fileName, 512, 512, false);
|
||||
}
|
||||
@@ -92,28 +92,99 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
[TestCase("test.bc7_fast")]
|
||||
[TestCase("test.bc7_normal")]
|
||||
[TestCase("test.bc7_best")]
|
||||
public void DecompressBC7Test(string fileName)
|
||||
public void Decompress_BC7(string fileName)
|
||||
{
|
||||
AssertCorrectBC7Decompression(TestFileFolders.BcTestFiles + fileName, 512, 512);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("Not implemented")]
|
||||
public void BC7PartialBlockTest([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
public void PartialBlock_BC1([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt1").AsSpan()[..Bc1.BlockSize];
|
||||
int bytesRead = Bc1.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartialBlock_BC2([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt3").AsSpan()[..Bc2.BlockSize];
|
||||
int bytesRead = Bc2.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartialBlock_BC3([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt5").AsSpan()[..Bc3.BlockSize];
|
||||
int bytesRead = Bc3.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartialBlock_BC4([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc4").AsSpan()[..Bc4.BlockSize];
|
||||
int bytesRead = Bc4.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartialBlock_BC5([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc5").AsSpan()[..Bc5.BlockSize];
|
||||
int bytesRead = Bc5.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartialBlock_BC6H([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc6h_best").AsSpan()[..Bc6h.BlockSize];
|
||||
int bytesRead = Bc6h.Decompress<ColorBGRA<byte>, byte>(data, width, height, false, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartialBlock_BC7([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc7_best").AsSpan()[..Bc7.BlockSize];
|
||||
int bytesRead = Bc7.Decompress(data, width, height, out byte[] decodedData);
|
||||
int bytesRead = Bc7.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA32>()));
|
||||
});
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertCorrectBC6HDecompression(string path, int width, int height, bool isSigned)
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(path);
|
||||
int bytesRead = Bc6h.Decompress(data, width, height, isSigned, out byte[] decodedData);
|
||||
int bytesRead = Bc6h.Decompress<ColorBGRA<byte>, byte>(data, width, height, isSigned, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
ByteArrayDeviation.AssertMinimalDeviation(decodedData, originalBgra32LogoData, MaxMeanDeviationBc6h, MaxStandardDeviationBc6h);
|
||||
}
|
||||
@@ -121,7 +192,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
private static void AssertCorrectBC7Decompression(string path, int width, int height)
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(path);
|
||||
int bytesRead = Bc7.Decompress(data, width, height, out byte[] decodedData);
|
||||
int bytesRead = Bc7.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
ByteArrayDeviation.AssertMinimalDeviation(decodedData, originalBgra32LogoData, MaxMeanDeviationBc7, MaxStandardDeviationBc7);
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ internal static class ByteArrayDeviation
|
||||
double standardDeviation = Math.Sqrt(sumOfSquaredDeviations / (decoded.Length - 1));
|
||||
//Not sure if Bessel's correction is needed here, but it doesn't hurt, especially since length is around 1 million in the current use.
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(mean, Is.LessThan(maxMeanDeviation), "Mean too far positive");
|
||||
Assert.That(mean, Is.GreaterThan(-maxMeanDeviation), "Mean too far negative");
|
||||
Assert.That(standardDeviation, Is.LessThan(maxStandardDeviation), "Standard deviation too large");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class DxtTests
|
||||
{
|
||||
@@ -9,7 +11,7 @@
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT1(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT1<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -22,7 +24,7 @@
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT3(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT3<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -35,7 +37,7 @@
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT5(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT5<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
|
||||
@@ -6,14 +6,14 @@ namespace AssetRipper.TextureDecoder.Tests;
|
||||
|
||||
public sealed class EtcTests
|
||||
{
|
||||
private static DecodingDelegate ETCDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressETC(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate ETC2Delegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressETC2(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate ETC2A1Delegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressETC2A1(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate ETC2A8Delegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressETC2A8(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate EACRSignedDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressEACRSigned(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate EACRUnsignedDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressEACRUnsigned(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate EACRGSignedDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressEACRGSigned(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate EACRGUnsignedDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressEACRGUnsigned(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate ETCDelegate { get; } = EtcDecoder.DecompressETC<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate ETC2Delegate { get; } = EtcDecoder.DecompressETC2<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate ETC2A1Delegate { get; } = EtcDecoder.DecompressETC2A1<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate ETC2A8Delegate { get; } = EtcDecoder.DecompressETC2A8<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate EACRSignedDelegate { get; } = EtcDecoder.DecompressEACRSigned<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate EACRUnsignedDelegate { get; } = EtcDecoder.DecompressEACRUnsigned<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate EACRGSignedDelegate { get; } = EtcDecoder.DecompressEACRGSigned<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate EACRGUnsignedDelegate { get; } = EtcDecoder.DecompressEACRGUnsigned<ColorBGRA<byte>, byte>;
|
||||
|
||||
[Test]
|
||||
public void DecompressETCTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc", ETCDelegate);
|
||||
@@ -57,7 +57,7 @@ public sealed class EtcTests
|
||||
[Test]
|
||||
public void CorrectnessEACRGUnsignedTest() => AssertCorrectDecompression<AndroidTextures.Logo_15, ColorRG<byte>>(EACRGUnsignedDelegate, .1, .6);
|
||||
|
||||
private delegate int DecodingDelegate(ArraySegment<byte> data, int width, int height, out byte[] decompressedData);
|
||||
private delegate int DecodingDelegate(ReadOnlySpan<byte> data, int width, int height, out byte[] decompressedData);
|
||||
|
||||
private static void AssertCorrectByteCountReadFor256SquareWithMips(string path, DecodingDelegate decoder)
|
||||
{
|
||||
@@ -65,7 +65,7 @@ public sealed class EtcTests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = decoder.Invoke(new ArraySegment<byte>(data, totalBytesRead, data.Length - totalBytesRead), size, size, out _);
|
||||
int bytesRead = decoder.Invoke(new ReadOnlySpan<byte>(data, totalBytesRead, data.Length - totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -79,7 +79,7 @@ public sealed class EtcTests
|
||||
private static void AssertCorrectDecompression<TTexture, TColor>(DecodingDelegate decoder, double maxMeanDeviation, double maxStandardDeviation) where TTexture : ITexture where TColor : unmanaged, IColor<TColor, byte>
|
||||
{
|
||||
byte[] data = TTexture.Data;
|
||||
int bytesRead = decoder.Invoke(new ArraySegment<byte>(data), TTexture.Width, TTexture.Height, out byte[] decompressedData);
|
||||
int bytesRead = decoder.Invoke(new ReadOnlySpan<byte>(data), TTexture.Width, TTexture.Height, out byte[] decompressedData);
|
||||
if (!TTexture.Mips)
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
@@ -88,12 +88,12 @@ public sealed class EtcTests
|
||||
byte[] comparisonData = AndroidTextures.Logo_00.Data;
|
||||
|
||||
//Remove unused color channels
|
||||
if (typeof(TColor) != typeof(ColorBGRA32) && typeof(TColor) != typeof(ColorRGBA<byte>))
|
||||
if (typeof(TColor) != typeof(ColorBGRA<byte>) && typeof(TColor) != typeof(ColorRGBA<byte>))
|
||||
{
|
||||
Span<ColorBGRA32> pixels = MemoryMarshal.Cast<byte, ColorBGRA32>((Span<byte>)comparisonData);
|
||||
Span<ColorBGRA<byte>> pixels = MemoryMarshal.Cast<byte, ColorBGRA<byte>>((Span<byte>)comparisonData);
|
||||
for (int i = 0; i < pixels.Length; i++)
|
||||
{
|
||||
pixels[i] = pixels[i].Convert<ColorBGRA32, byte, TColor, byte>().Convert<TColor, byte, ColorBGRA32, byte>();
|
||||
pixels[i] = pixels[i].Convert<ColorBGRA<byte>, byte, TColor, byte>().Convert<TColor, byte, ColorBGRA<byte>, byte>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,12 +57,12 @@ public partial class ColorARGB16Tests
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -73,12 +73,12 @@ public partial class ColorARGB16Tests
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -89,12 +89,12 @@ public partial class ColorARGB16Tests
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -105,12 +105,12 @@ public partial class ColorARGB16Tests
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -118,13 +118,13 @@ public partial class ColorARGB16Tests
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -133,31 +133,19 @@ public partial class ColorARGB16Tests
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public static ColorARGB16 MakeRandomColor() => ColorRandom<ColorARGB16, byte>.MakeRandomColor();
|
||||
|
||||
public static byte MakeRandomValue() => ColorRandom<ColorARGB16, byte>.MakeRandomValue();
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorARGB32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB16, byte, ColorARGB32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorBGRA32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB16, byte, ColorBGRA32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_sbyte()
|
||||
{
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorARGB32Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorARGB32>(), Is.EqualTo(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorARGB32 MakeRandomColor() => ColorRandom<ColorARGB32, byte>.MakeRandomColor();
|
||||
|
||||
public static byte MakeRandomValue() => ColorRandom<ColorARGB32, byte>.MakeRandomValue();
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorARGB32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorARGB32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorBGRA32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorBGRA32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_sbyte()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<sbyte>, sbyte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_byte()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<byte>, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_short()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<short>, short>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_ushort()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<ushort>, ushort>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_int()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<int>, int>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_uint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<uint>, uint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<nint>, nint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nuint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<nuint>, nuint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_long()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<long>, long>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_ulong()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<ulong>, ulong>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_Int128()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<Int128>, Int128>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_UInt128()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<UInt128>, UInt128>();
|
||||
}
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorBGRA32Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorBGRA32>(), Is.EqualTo(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorBGRA32 MakeRandomColor() => ColorRandom<ColorBGRA32, byte>.MakeRandomColor();
|
||||
|
||||
public static byte MakeRandomValue() => ColorRandom<ColorBGRA32, byte>.MakeRandomValue();
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorARGB32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorARGB32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorBGRA32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorBGRA32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_sbyte()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<sbyte>, sbyte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_byte()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<byte>, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_short()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<short>, short>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_ushort()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<ushort>, ushort>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_int()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<int>, int>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_uint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<uint>, uint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<nint>, nint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nuint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<nuint>, nuint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_long()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<long>, long>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_ulong()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<ulong>, ulong>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_Int128()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<Int128>, Int128>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_UInt128()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<UInt128>, UInt128>();
|
||||
}
|
||||
}
|
||||
@@ -57,12 +57,12 @@ public partial class ColorRGB16Tests
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -73,12 +73,12 @@ public partial class ColorRGB16Tests
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -89,12 +89,12 @@ public partial class ColorRGB16Tests
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -105,12 +105,12 @@ public partial class ColorRGB16Tests
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -118,13 +118,13 @@ public partial class ColorRGB16Tests
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -133,31 +133,19 @@ public partial class ColorRGB16Tests
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public static ColorRGB16 MakeRandomColor() => ColorRandom<ColorRGB16, byte>.MakeRandomColor();
|
||||
|
||||
public static byte MakeRandomValue() => ColorRandom<ColorRGB16, byte>.MakeRandomValue();
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorARGB32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorARGB32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorBGRA32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorBGRA32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_sbyte()
|
||||
{
|
||||
|
||||
@@ -18,13 +18,13 @@ public partial class ColorRGB9e5Tests
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -33,13 +33,13 @@ public partial class ColorRGB9e5Tests
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public static ColorRGB9e5 MakeRandomColor() => ColorRandom<ColorRGB9e5, double>.MakeRandomColor();
|
||||
|
||||
@@ -57,12 +57,12 @@ public partial class ColorRGBA16Tests
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -73,12 +73,12 @@ public partial class ColorRGBA16Tests
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -89,12 +89,12 @@ public partial class ColorRGBA16Tests
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -105,12 +105,12 @@ public partial class ColorRGBA16Tests
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -118,13 +118,13 @@ public partial class ColorRGBA16Tests
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -133,31 +133,19 @@ public partial class ColorRGBA16Tests
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public static ColorRGBA16 MakeRandomColor() => ColorRandom<ColorRGBA16, byte>.MakeRandomColor();
|
||||
|
||||
public static byte MakeRandomValue() => ColorRandom<ColorRGBA16, byte>.MakeRandomValue();
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorARGB32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGBA16, byte, ColorARGB32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorBGRA32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGBA16, byte, ColorBGRA32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_sbyte()
|
||||
{
|
||||
|
||||
@@ -55,12 +55,12 @@ internal partial class GenericColorTests<TColor, TChannelValue> where TColor : u
|
||||
TChannelValue b = color.B;
|
||||
TChannelValue a = color.A;
|
||||
color.R = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -71,12 +71,12 @@ internal partial class GenericColorTests<TColor, TChannelValue> where TColor : u
|
||||
TChannelValue b = color.B;
|
||||
TChannelValue a = color.A;
|
||||
color.G = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -87,12 +87,12 @@ internal partial class GenericColorTests<TColor, TChannelValue> where TColor : u
|
||||
TChannelValue g = color.G;
|
||||
TChannelValue a = color.A;
|
||||
color.B = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -103,12 +103,12 @@ internal partial class GenericColorTests<TColor, TChannelValue> where TColor : u
|
||||
TChannelValue g = color.G;
|
||||
TChannelValue b = color.B;
|
||||
color.A = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -116,13 +116,13 @@ internal partial class GenericColorTests<TColor, TChannelValue> where TColor : u
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
color.GetChannels(out TChannelValue r, out TChannelValue g, out TChannelValue b, out TChannelValue a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -131,13 +131,13 @@ internal partial class GenericColorTests<TColor, TChannelValue> where TColor : u
|
||||
TColor color = MakeRandomColor();
|
||||
color.GetChannels(out TChannelValue r, out TChannelValue g, out TChannelValue b, out TChannelValue a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static TChannelValue MakeRandomValue() => ColorRandom<TColor, TChannelValue>.MakeRandomValue();
|
||||
|
||||
@@ -8,86 +8,120 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<sbyte>), typeof(sbyte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<sbyte>), typeof(sbyte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<sbyte>), typeof(sbyte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<sbyte>), typeof(sbyte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<sbyte>), typeof(sbyte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<decimal>), typeof(decimal) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<decimal>), typeof(decimal) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<decimal>), typeof(decimal) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<decimal>), typeof(decimal) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<decimal>), typeof(decimal) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<decimal>), typeof(decimal) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<decimal>), typeof(decimal) })]
|
||||
partial class GenericColorTests<TColor, TChannelValue>
|
||||
{
|
||||
}
|
||||
+20
-20
@@ -9,26 +9,26 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = [typeof(long), typeof(ulong)])]
|
||||
[TestFixture(TypeArgs = [typeof(Int128), typeof(UInt128)])]
|
||||
internal class NumericConversionExtremesTests<TSigned, TUnsigned>
|
||||
where TSigned : unmanaged, IMinMaxValue<TSigned>
|
||||
where TUnsigned : unmanaged, IMinMaxValue<TUnsigned>
|
||||
where TSigned : unmanaged, IMinMaxValue<TSigned>
|
||||
where TUnsigned : unmanaged, IMinMaxValue<TUnsigned>
|
||||
{
|
||||
[Test]
|
||||
public void SignedToUnsignedExtremesAreCorrect()
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(NumericConversion.Convert<TSigned, TUnsigned>(TSigned.MinValue), Is.EqualTo(TUnsigned.MinValue));
|
||||
Assert.That(NumericConversion.Convert<TSigned, TUnsigned>(TSigned.MaxValue), Is.EqualTo(TUnsigned.MaxValue));
|
||||
});
|
||||
}
|
||||
[Test]
|
||||
public void SignedToUnsignedExtremesAreCorrect()
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(NumericConversion.Convert<TSigned, TUnsigned>(TSigned.MinValue), Is.EqualTo(TUnsigned.MinValue));
|
||||
Assert.That(NumericConversion.Convert<TSigned, TUnsigned>(TSigned.MaxValue), Is.EqualTo(TUnsigned.MaxValue));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnsignedToSignedExtremesAreCorrect()
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(NumericConversion.Convert<TUnsigned, TSigned>(TUnsigned.MinValue), Is.EqualTo(TSigned.MinValue));
|
||||
Assert.That(NumericConversion.Convert<TUnsigned, TSigned>(TUnsigned.MaxValue), Is.EqualTo(TSigned.MaxValue));
|
||||
});
|
||||
}
|
||||
[Test]
|
||||
public void UnsignedToSignedExtremesAreCorrect()
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(NumericConversion.Convert<TUnsigned, TSigned>(TUnsigned.MinValue), Is.EqualTo(TSigned.MinValue));
|
||||
Assert.That(NumericConversion.Convert<TUnsigned, TSigned>(TUnsigned.MaxValue), Is.EqualTo(TSigned.MaxValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using AssetRipper.TextureDecoder.Pvrtc;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests;
|
||||
|
||||
@@ -11,7 +12,7 @@ public sealed class PvrtcTests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = PvrtcDecoder.DecompressPVRTC(data, size, size, false, out _);
|
||||
int bytesRead = PvrtcDecoder.DecompressPVRTC<ColorBGRA<byte>, byte>(data, size, size, false, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -32,7 +33,7 @@ public sealed class PvrtcTests
|
||||
private static void AssertCorrectDecompression<T>(bool do2bitMode, double maxMeanDeviation, double maxStandardDeviation) where T : ITexture
|
||||
{
|
||||
ReadOnlySpan<byte> data = T.Data;
|
||||
int bytesRead = PvrtcDecoder.DecompressPVRTC(data, T.Width, T.Height, do2bitMode, out byte[] decompressedData);
|
||||
int bytesRead = PvrtcDecoder.DecompressPVRTC<ColorBGRA<byte>, byte>(data, T.Width, T.Height, do2bitMode, out byte[] decompressedData);
|
||||
if (!T.Mips)
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
|
||||
+38
-38
@@ -3,7 +3,7 @@ using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class NewRgbTests
|
||||
public sealed class RgbConsistencyTests
|
||||
{
|
||||
[Test]
|
||||
public void ConvertA8Test()
|
||||
@@ -12,8 +12,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
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);
|
||||
RgbConverter.Convert<ColorA<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
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);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.Convert<ColorARGB16, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = RgbConverter.Convert<ColorARGB16, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -40,8 +40,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
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);
|
||||
RgbConverter.Convert<ColorRGB<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
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);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -55,8 +55,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
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);
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
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);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -70,8 +70,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
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);
|
||||
RgbConverter.Convert<ColorARGB32, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
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);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -85,8 +85,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
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);
|
||||
RgbConverter.Convert<ColorRGB16, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
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);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -100,7 +100,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.Convert<ColorR<ushort>, ushort, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = RgbConverter.Convert<ColorR<ushort>, ushort, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -113,7 +113,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.Convert<ColorRGBA16, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = RgbConverter.Convert<ColorRGBA16, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -126,8 +126,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
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);
|
||||
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
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);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -141,8 +141,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
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);
|
||||
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
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);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -153,8 +153,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rhalf");
|
||||
RgbConverter.RHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorR<Half>, Half, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorR<Half>, Half, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -162,8 +162,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rghalf");
|
||||
RgbConverter.RGHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<Half>, Half, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<Half>, Half, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -171,8 +171,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGBAHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbahalf");
|
||||
RgbConverter.RGBAHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<Half>, Half, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGBAHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<Half>, Half, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -180,8 +180,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rfloat");
|
||||
RgbConverter.RFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorR<float>, float, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorR<float>, float, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -189,8 +189,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgfloat");
|
||||
RgbConverter.RGFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<float>, float, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<float>, float, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -198,8 +198,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGBAFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbafloat");
|
||||
RgbConverter.RGBAFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<float>, float, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGBAFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<float>, float, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -207,8 +207,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGB9e5FloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb9e5float");
|
||||
RgbConverter.RGB9e5FloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGB9e5FloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -216,8 +216,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRG32Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rg32");
|
||||
RgbConverter.RG32ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<ushort>, ushort, ColorBGRA32, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
OldRgbConverter.RG32ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<ushort>, ushort, ColorBGRA<byte>, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -225,8 +225,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGB48Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb48");
|
||||
RgbConverter.RGB48ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB<ushort>, ushort, ColorBGRA32, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGB48ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB<ushort>, ushort, ColorBGRA<byte>, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -234,8 +234,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGBA64Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgba64");
|
||||
RgbConverter.RGBA64ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<ushort>, ushort, ColorBGRA32, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGBA64ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<ushort>, ushort, ColorBGRA<byte>, byte>(data, 512, 512, out byte[] 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
|
||||
{
|
||||
@@ -9,7 +12,7 @@
|
||||
int totalBytesRead = 0;
|
||||
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;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -22,7 +25,7 @@
|
||||
int totalBytesRead = 0;
|
||||
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;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -35,7 +38,7 @@
|
||||
int totalBytesRead = 0;
|
||||
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;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -48,7 +51,7 @@
|
||||
int totalBytesRead = 0;
|
||||
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;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -61,7 +64,7 @@
|
||||
int totalBytesRead = 0;
|
||||
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;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -74,7 +77,7 @@
|
||||
int totalBytesRead = 0;
|
||||
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;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -87,7 +90,7 @@
|
||||
int totalBytesRead = 0;
|
||||
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;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -97,7 +100,7 @@
|
||||
public void ConvertRHalfTest()
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -105,7 +108,7 @@
|
||||
public void ConvertRGHalfTest()
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -113,7 +116,7 @@
|
||||
public void ConvertRGBAHalfTest()
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -121,7 +124,7 @@
|
||||
public void ConvertRFloatTest()
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -129,7 +132,7 @@
|
||||
public void ConvertRGFloatTest()
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -137,7 +140,7 @@
|
||||
public void ConvertRGBAFloatTest()
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -145,7 +148,7 @@
|
||||
public void ConvertRGB9e5FloatTest()
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -153,7 +156,7 @@
|
||||
public void ConvertRG32Test()
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -161,7 +164,7 @@
|
||||
public void ConvertRGB48Test()
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -169,8 +172,15 @@
|
||||
public void ConvertRGBA64Test()
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
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,12 +13,12 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void DecompressYUY2Test()
|
||||
{
|
||||
byte[] input = File.ReadAllBytes(TestFileFolders.Yuy2TestFiles + "test.yuy2");
|
||||
int bytesRead = Yuy2Decoder.DecompressYUY2<ColorBGRA32, byte>(input, TestImageWidth, TestImageHeight, out byte[] decodedData);
|
||||
Assert.Multiple(() =>
|
||||
int bytesRead = Yuy2Decoder.DecompressYUY2<ColorBGRA<byte>, byte>(input, TestImageWidth, TestImageHeight, out byte[] decodedData);
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(input.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(TestImageWidth * TestImageHeight * Unsafe.SizeOf<ColorBGRA32>()));
|
||||
});
|
||||
Assert.That(decodedData, Has.Length.EqualTo(TestImageWidth * TestImageHeight * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<Solution>
|
||||
<Folder Name="/Solution Items/">
|
||||
<File Path=".editorconfig" />
|
||||
<File Path=".gitignore" />
|
||||
<File Path="Directory.Build.props" />
|
||||
<File Path="LICENSE" />
|
||||
<File Path="README.md" />
|
||||
</Folder>
|
||||
<Folder Name="/Solution Items/Licenses/">
|
||||
<File Path="Licenses/bcdec.md" />
|
||||
<File Path="Licenses/uTinyRipper.md" />
|
||||
</Folder>
|
||||
<Folder Name="/Solution Items/Workflows/">
|
||||
<File Path=".github/workflows/build.yml" />
|
||||
</Folder>
|
||||
<Project Path="AssetRipper.TextureDecoder.ColorGenerator/AssetRipper.TextureDecoder.ColorGenerator.csproj" />
|
||||
<Project Path="AssetRipper.TextureDecoder.ConsoleApp/AssetRipper.TextureDecoder.ConsoleApp.csproj" />
|
||||
<Project Path="AssetRipper.TextureDecoder.Paths/AssetRipper.TextureDecoder.Paths.csproj" />
|
||||
<Project Path="AssetRipper.TextureDecoder.SourceGeneration.Common/AssetRipper.TextureDecoder.SourceGeneration.Common.csproj" />
|
||||
<Project Path="AssetRipper.TextureDecoder.TestGenerator/AssetRipper.TextureDecoder.TestGenerator.csproj" />
|
||||
<Project Path="AssetRipper.TextureDecoder.Tests/AssetRipper.TextureDecoder.Tests.csproj" />
|
||||
<Project Path="AssetRipper.TextureDecoder/AssetRipper.TextureDecoder.csproj" />
|
||||
<Project Path="IntegerConversion.ConstantEstimation/IntegerConversion.ConstantEstimation.csproj" />
|
||||
</Solution>
|
||||
@@ -1,16 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
||||
|
||||
<PackageId>AssetRipper.TextureDecoder</PackageId>
|
||||
<PackageTags>C# Texture</PackageTags>
|
||||
<RepositoryUrl>https://github.com/AssetRipper/TextureDecoder</RepositoryUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<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>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.103">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Astc
|
||||
@@ -12,51 +14,58 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
public static int DecodeASTC(ReadOnlySpan<byte> input, int width, int height, int blockWidth, int blockHeight, out byte[] output)
|
||||
public static int DecodeASTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, int blockWidth, int blockHeight, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecodeASTC(input, width, height, blockWidth, blockHeight, output);
|
||||
return DecodeASTC<TOutputColor, TOutputChannelValue>(input, width, height, blockWidth, blockHeight, output);
|
||||
}
|
||||
|
||||
public static int DecodeASTC(ReadOnlySpan<byte> input, int width, int height, int blockWidth, int blockHeight, Span<byte> output)
|
||||
public static int DecodeASTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, int blockWidth, int blockHeight, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecodeASTC<TOutputColor, TOutputChannelValue>(input, width, height, blockWidth, blockHeight, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecodeASTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, int blockWidth, int blockHeight, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
ValidateBlockDimension(blockWidth);
|
||||
ValidateBlockDimension(blockHeight);
|
||||
int bcw = (width + blockWidth - 1) / blockWidth;
|
||||
int bch = (height + blockHeight - 1) / blockHeight;
|
||||
int clen_last = (width + blockWidth - 1) % blockWidth + 1;
|
||||
Span<uint> buf = stackalloc uint[blockWidth * blockHeight];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[blockWidth * blockHeight];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeBlock(input[inputOffset..], blockWidth, blockHeight, buf);
|
||||
DecodeBlock(input[inputOffset..], blockWidth, blockHeight, buffer);
|
||||
int clen = s < bcw - 1 ? blockWidth : clen_last;
|
||||
for (int i = 0, y = t * blockHeight; i < blockHeight && y < height; i++, y++)
|
||||
{
|
||||
int outputOffsetUInt32 = t * blockHeight * width + s * blockWidth + i * width;
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
WriteUInt32(output, outputOffsetUInt32 + j, buf[j + i * blockWidth]);
|
||||
output[outputOffsetUInt32 + j] = buffer[j + i * blockWidth].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
static void WriteUInt32(Span<byte> buffer, int offsetUInt32, uint value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offsetUInt32 * sizeof(uint)), value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeBlock(ReadOnlySpan<byte> input, int blockWidth, int blockHeight, Span<uint> output)
|
||||
private static void DecodeBlock(ReadOnlySpan<byte> input, int blockWidth, int blockHeight, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
if (input[0] == 0xfc && (input[1] & 1) == 1)
|
||||
{
|
||||
uint c = Color(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[i] = c;
|
||||
@@ -674,7 +683,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplicateColor(BlockData block, Span<uint> output)
|
||||
private static void ApplicateColor(BlockData block, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
if (block.dual_plane != 0)
|
||||
{
|
||||
@@ -689,7 +698,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
byte g = SelectColor(block.endpoints[p][1], block.endpoints[p][5], block.weights[i * 2 + ps[1]]);
|
||||
byte b = SelectColor(block.endpoints[p][2], block.endpoints[p][6], block.weights[i * 2 + ps[2]]);
|
||||
byte a = SelectColor(block.endpoints[p][3], block.endpoints[p][7], block.weights[i * 2 + ps[3]]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
output[i] = new(r, g, b, a);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -700,7 +709,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
byte g = SelectColor(block.endpoints[0][1], block.endpoints[0][5], block.weights[i * 2 + ps[1]]);
|
||||
byte b = SelectColor(block.endpoints[0][2], block.endpoints[0][6], block.weights[i * 2 + ps[2]]);
|
||||
byte a = SelectColor(block.endpoints[0][3], block.endpoints[0][7], block.weights[i * 2 + ps[3]]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
output[i] = new(r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -713,7 +722,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
byte g = SelectColor(block.endpoints[p][1], block.endpoints[p][5], block.weights[i * 2]);
|
||||
byte b = SelectColor(block.endpoints[p][2], block.endpoints[p][6], block.weights[i * 2]);
|
||||
byte a = SelectColor(block.endpoints[p][3], block.endpoints[p][7], block.weights[i * 2]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
output[i] = new(r, g, b, a);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -724,7 +733,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
byte g = SelectColor(block.endpoints[0][1], block.endpoints[0][5], block.weights[i * 2]);
|
||||
byte b = SelectColor(block.endpoints[0][2], block.endpoints[0][6], block.weights[i * 2]);
|
||||
byte a = SelectColor(block.endpoints[0][3], block.endpoints[0][7], block.weights[i * 2]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
output[i] = new(r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -849,12 +858,6 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static uint Color(uint r, uint g, uint b, uint a)
|
||||
{
|
||||
return r << 16 | g << 8 | b | a << 24;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static byte BitReverseU8(byte c, int bits)
|
||||
{
|
||||
|
||||
@@ -1,34 +1,48 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Atc
|
||||
{
|
||||
public static class AtcDecoder
|
||||
{
|
||||
public static int DecompressAtcRgb4(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressAtcRgb4<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressAtcRgb4(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressAtcRgb4<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressAtcRgb4(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressAtcRgb4<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressAtcRgb4<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressAtcRgb4<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<TOutputColor> buf = stackalloc TOutputColor[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeAtcRgb4Block(input.Slice(inputOffset, 8), buf);
|
||||
DecodeAtcRgb4Block<TOutputColor, TOutputChannelValue>(input.Slice(inputOffset, 8), buf);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
int outputOffset = t * 16 * width + s * 16 + i * width * sizeof(uint);
|
||||
int outputOffset = t * 4 * width + s * 4 + i * width;
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(output.Slice(outputOffset + j * sizeof(uint)), buf[j + 4 * i]);
|
||||
output[outputOffset + j] = buf[j + 4 * i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,31 +50,44 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressAtcRgba8(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressAtcRgba8<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressAtcRgba8(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressAtcRgba8<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressAtcRgba8(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressAtcRgba8<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressAtcRgba8<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressAtcRgba8<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<TOutputColor> buf = stackalloc TOutputColor[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeAtcRgba8Block(input.Slice(inputOffset, 16), buf);
|
||||
DecodeAtcRgba8Block<TOutputColor, TOutputChannelValue>(input.Slice(inputOffset, 16), buf);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
int outputOffset = t * 16 * width + s * 16 + i * width * sizeof(uint);
|
||||
int outputOffset = t * 4 * width + s * 4 + i * width;
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(output.Slice(outputOffset + j * sizeof(uint)), buf[j + 4 * i]);
|
||||
output[outputOffset + j] = buf[j + 4 * i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,7 +95,9 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
private static void DecodeAtcRgb4Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeAtcRgb4Block<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
Span<int> colors = stackalloc int[16];
|
||||
int c0 = input.ReadAtOffset<ushort>(0);
|
||||
@@ -83,12 +112,14 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
int cb = colors[cidx * 4 + 0];
|
||||
int cg = colors[cidx * 4 + 1];
|
||||
int cr = colors[cidx * 4 + 2];
|
||||
output[i] = Color(cr, cg, cb, 255);
|
||||
output[i].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>((byte)cr, (byte)cg, (byte)cb, byte.MaxValue);
|
||||
cindex >>= 2;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeAtcRgba8Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeAtcRgba8Block<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
Span<int> alphas = stackalloc int[16];
|
||||
ulong avalue = BinaryPrimitives.ReadUInt64LittleEndian(input);
|
||||
@@ -112,7 +143,7 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
int cr = colors[cidx * 4 + 2];
|
||||
int aidx = unchecked((int)aindex & 7);
|
||||
int ca = alphas[aidx];
|
||||
output[i] = Color(cr, cg, cb, ca);
|
||||
output[i].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>((byte)cr, (byte)cg, (byte)cb, (byte)ca);
|
||||
cindex >>= 2;
|
||||
aindex >>= 3;
|
||||
}
|
||||
@@ -121,6 +152,7 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static T ReadAtOffset<T>(this ReadOnlySpan<byte> input, int offset) where T : unmanaged
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return MemoryMarshal.Read<T>(input.Slice(offset));
|
||||
}
|
||||
|
||||
@@ -159,9 +191,9 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
colors[13] = Extend((c1 >> 5) & 0x3F, 6, 8);
|
||||
colors[14] = Extend((c1 >> 11) & 0x1F, 5, 8);
|
||||
|
||||
colors[4] = Math.Max(0, colors[8] - colors[12] / 4);
|
||||
colors[5] = Math.Max(0, colors[9] - colors[13] / 4);
|
||||
colors[6] = Math.Max(0, colors[10] - colors[14] / 4);
|
||||
colors[4] = int.Max(0, colors[8] - colors[12] / 4);
|
||||
colors[5] = int.Max(0, colors[9] - colors[13] / 4);
|
||||
colors[6] = int.Max(0, colors[10] - colors[14] / 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,11 +228,5 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
// bit-pattern replicating scaling (can at most double the bits)
|
||||
return (value << (to - from)) | (value >> (from * 2 - to));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static uint Color(int r, int g, int b, int a)
|
||||
{
|
||||
return unchecked((uint)(r << 16 | g << 8 | b | a << 24));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,26 +9,56 @@ public static class Bc1
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 8;
|
||||
/// <summary>
|
||||
/// The width of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockWidth = 4;
|
||||
/// <summary>
|
||||
/// The height of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockHeight = 4;
|
||||
/// <summary>
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
BcHelpers.DecompressBc1(input.Slice(inputOffset, BlockSize), output.Slice(outputOffset), width * 4);
|
||||
BcHelpers.DecompressBc1(input.Slice(inputOffset, BlockSize), buffer);
|
||||
BcHelpers.CopyBufferToOutput<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorRGBA<byte>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(output, width, height, output);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,26 +9,56 @@ public static class Bc2
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The width of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockWidth = 4;
|
||||
/// <summary>
|
||||
/// The height of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockHeight = 4;
|
||||
/// <summary>
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
BcHelpers.DecompressBc2(input.Slice(inputOffset), output.Slice(outputOffset), width * 4);
|
||||
BcHelpers.DecompressBc2(input.Slice(inputOffset, BlockSize), buffer);
|
||||
BcHelpers.CopyBufferToOutput<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorRGBA<byte>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(output, width, height, output);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,26 +9,56 @@ public static class Bc3
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The width of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockWidth = 4;
|
||||
/// <summary>
|
||||
/// The height of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockHeight = 4;
|
||||
/// <summary>
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
BcHelpers.DecompressBc3(input.Slice(inputOffset), output.Slice(outputOffset), width * 4);
|
||||
BcHelpers.DecompressBc3(input.Slice(inputOffset, BlockSize), buffer);
|
||||
BcHelpers.CopyBufferToOutput<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorRGBA<byte>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(output, width, height, output);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,30 +10,56 @@ public static class Bc4
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 8;
|
||||
/// <summary>
|
||||
/// The width of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockWidth = 4;
|
||||
/// <summary>
|
||||
/// The height of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockHeight = 4;
|
||||
/// <summary>
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorR<byte>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
int bufferSize = width * height * Unsafe.SizeOf<ColorR<byte>>();
|
||||
byte[] bufferArray = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
Span<byte> buffer = new Span<byte>(bufferArray, 0, bufferSize);
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int bufferOffset = ((i * width) + j) * Unsafe.SizeOf<ColorR<byte>>();
|
||||
BcHelpers.DecompressBc4(input.Slice(inputOffset), buffer.Slice(bufferOffset), width);
|
||||
BcHelpers.DecompressBc4(input.Slice(inputOffset, BlockSize), buffer);
|
||||
BcHelpers.CopyBufferToOutput<ColorR<byte>, byte, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorR<byte>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA32, byte>(buffer, width, height, output);
|
||||
ArrayPool<byte>.Shared.Return(bufferArray);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,30 +10,56 @@ public static class Bc5
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The width of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockWidth = 4;
|
||||
/// <summary>
|
||||
/// The height of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockHeight = 4;
|
||||
/// <summary>
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorRG<byte>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
int bufferSize = width * height * Unsafe.SizeOf<ColorRG<byte>>();
|
||||
byte[] bufferArray = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
Span<byte> buffer = new Span<byte>(bufferArray, 0, bufferSize);
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int bufferOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRG<byte>>();
|
||||
BcHelpers.DecompressBc5(input.Slice(inputOffset), buffer.Slice(bufferOffset), width * 2);
|
||||
BcHelpers.DecompressBc5(input.Slice(inputOffset, BlockSize), buffer);
|
||||
BcHelpers.CopyBufferToOutput<ColorRG<byte>, byte, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorRG<byte>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA32, byte>(buffer, width, height, output);
|
||||
ArrayPool<byte>.Shared.Return(bufferArray);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,34 +10,56 @@ public static class Bc6h
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The width of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockWidth = 4;
|
||||
/// <summary>
|
||||
/// The height of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockHeight = 4;
|
||||
/// <summary>
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorRGB<Half>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, bool isSigned, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, bool isSigned, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, isSigned, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, isSigned, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, bool isSigned, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, bool isSigned, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
int bufferSize = width * height * Unsafe.SizeOf<ColorRGB<Half>>();
|
||||
byte[] bufferArray = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
Span<byte> buffer = new Span<byte>(bufferArray, 0, bufferSize);
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, isSigned, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, bool isSigned, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGB<Half>>();
|
||||
BcHelpers.DecompressBc6h(
|
||||
input.Slice(inputOffset, BlockSize),
|
||||
buffer.Slice(outputOffset),
|
||||
width * 3,
|
||||
isSigned);
|
||||
BcHelpers.DecompressBc6h(input.Slice(inputOffset, BlockSize), buffer, isSigned);
|
||||
BcHelpers.CopyBufferToOutput<ColorRGB<Half>, Half, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorRGB<Half>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGB<Half>, Half, ColorBGRA32, byte>(buffer, width, height, output);
|
||||
ArrayPool<byte>.Shared.Return(bufferArray);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,29 +21,44 @@ public static class Bc7
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * PixelSize;
|
||||
BcHelpers.DecompressBc7(
|
||||
input.Slice(inputOffset, BlockSize),
|
||||
output.Slice(outputOffset),
|
||||
width * 4);
|
||||
BcHelpers.DecompressBc7(input.Slice(inputOffset, BlockSize), buffer);
|
||||
BcHelpers.CopyBufferToOutput<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorRGBA<byte>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(output, width, height, output);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,39 +1,53 @@
|
||||
using System.Buffers.Binary;
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using System.Buffers.Binary;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
internal static class BcHelpers
|
||||
{
|
||||
public static void DecompressBc1(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock, int destinationPitch)
|
||||
public static void DecompressBc1(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock)
|
||||
{
|
||||
Debug.Assert(decompressedBlock.Length == Bc1.DecodedBlockSize);
|
||||
int destinationPitch = 4 * 4;
|
||||
ColorBlock(compressedBlock, decompressedBlock, destinationPitch, 0);
|
||||
}
|
||||
|
||||
public static void DecompressBc2(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock, int destinationPitch)
|
||||
public static void DecompressBc2(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock)
|
||||
{
|
||||
Debug.Assert(decompressedBlock.Length == Bc2.DecodedBlockSize);
|
||||
int destinationPitch = 4 * 4;
|
||||
ColorBlock(compressedBlock.Slice(8), decompressedBlock, destinationPitch, 1);
|
||||
SharpAlphaBlock(compressedBlock, decompressedBlock.Slice(3), destinationPitch);
|
||||
}
|
||||
|
||||
public static void DecompressBc3(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock, int destinationPitch)
|
||||
public static void DecompressBc3(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock)
|
||||
{
|
||||
Debug.Assert(decompressedBlock.Length == Bc3.DecodedBlockSize);
|
||||
int destinationPitch = 4 * 4;
|
||||
ColorBlock(compressedBlock.Slice(8), decompressedBlock, destinationPitch, 1);
|
||||
SmoothAlphaBlock(compressedBlock, decompressedBlock.Slice(3), destinationPitch, 4);
|
||||
}
|
||||
|
||||
public static void DecompressBc4(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock, int destinationPitch)
|
||||
public static void DecompressBc4(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock)
|
||||
{
|
||||
Debug.Assert(decompressedBlock.Length == Bc4.DecodedBlockSize);
|
||||
int destinationPitch = 4 * 1;
|
||||
SmoothAlphaBlock(compressedBlock, decompressedBlock, destinationPitch, 1);
|
||||
}
|
||||
|
||||
public static void DecompressBc5(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock, int destinationPitch)
|
||||
public static void DecompressBc5(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock)
|
||||
{
|
||||
Debug.Assert(decompressedBlock.Length == Bc5.DecodedBlockSize);
|
||||
int destinationPitch = 4 * 2;
|
||||
SmoothAlphaBlock(compressedBlock, decompressedBlock, destinationPitch, 2);
|
||||
SmoothAlphaBlock(compressedBlock.Slice(8), decompressedBlock.Slice(1), destinationPitch, 2);
|
||||
}
|
||||
|
||||
public static void DecompressBc6h(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock, int destinationPitch, bool isSigned)
|
||||
public static void DecompressBc6h(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock, bool isSigned)
|
||||
{
|
||||
Debug.Assert(decompressedBlock.Length == Bc6h.DecodedBlockSize);
|
||||
|
||||
BitStream bstream = new(compressedBlock);
|
||||
Span<uint> r = stackalloc uint[4]; // wxyz
|
||||
Span<uint> g = stackalloc uint[4];
|
||||
@@ -448,15 +462,7 @@ internal static class BcHelpers
|
||||
// Do not use these in your encoder. If the hardware is passed blocks
|
||||
// with one of these modes specified, the resulting decompressed block
|
||||
// must contain all zeroes in all channels except for the alpha channel.
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
Span<byte> pixel = decompressedBlock.Slice(decompressedOffset + (j * 3 * sizeof(ushort)), 3 * sizeof(ushort));
|
||||
pixel.Clear();
|
||||
}
|
||||
decompressedOffset += destinationPitch * sizeof(ushort);
|
||||
}
|
||||
decompressedBlock.Clear();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -535,7 +541,7 @@ internal static class BcHelpers
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(decompressedBlock.Slice(bOffset), bFinal);
|
||||
}
|
||||
|
||||
decompressedOffset += destinationPitch * sizeof(ushort);
|
||||
decompressedOffset += 4 * 3 * sizeof(ushort);
|
||||
}
|
||||
|
||||
static uint ReadBc6hModeBits(ref BitStream bstream)
|
||||
@@ -553,30 +559,20 @@ internal static class BcHelpers
|
||||
}
|
||||
}
|
||||
|
||||
public static void DecompressBc7(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock, int destinationPitch)
|
||||
public static void DecompressBc7(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock)
|
||||
{
|
||||
Debug.Assert(decompressedBlock.Length == Bc7.DecodedBlockSize);
|
||||
|
||||
BitStream bstream = new(compressedBlock);
|
||||
Span2D<uint> endpoints = new(stackalloc uint[6 * 4], 6, 4);
|
||||
Span2D<int> indices = new(stackalloc int[4 * 4], 4, 4);
|
||||
|
||||
int decompressedOffset = 0;
|
||||
|
||||
int mode = ReadBc7Mode(ref bstream);
|
||||
|
||||
// unexpected mode, clear the block (transparent black)
|
||||
if (mode >= 8)
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
decompressedBlock[decompressedOffset + (j * 4) + 0] = 0;
|
||||
decompressedBlock[decompressedOffset + (j * 4) + 1] = 0;
|
||||
decompressedBlock[decompressedOffset + (j * 4) + 2] = 0;
|
||||
decompressedBlock[decompressedOffset + (j * 4) + 3] = 0;
|
||||
}
|
||||
decompressedOffset += destinationPitch;
|
||||
}
|
||||
decompressedBlock.Clear();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -814,13 +810,12 @@ internal static class BcHelpers
|
||||
break;
|
||||
}
|
||||
|
||||
decompressedBlock[decompressedOffset + (j * 4) + 0] = (byte)r;
|
||||
decompressedBlock[decompressedOffset + (j * 4) + 1] = (byte)g;
|
||||
decompressedBlock[decompressedOffset + (j * 4) + 2] = (byte)b;
|
||||
decompressedBlock[decompressedOffset + (j * 4) + 3] = (byte)a;
|
||||
int offset = (i * 16) + (j * 4);
|
||||
decompressedBlock[offset + 0] = (byte)r;
|
||||
decompressedBlock[offset + 1] = (byte)g;
|
||||
decompressedBlock[offset + 2] = (byte)b;
|
||||
decompressedBlock[offset + 3] = (byte)a;
|
||||
}
|
||||
|
||||
decompressedOffset += destinationPitch;
|
||||
}
|
||||
|
||||
static int ReadBc7Mode(ref BitStream bstream)
|
||||
@@ -1096,4 +1091,26 @@ internal static class BcHelpers
|
||||
{
|
||||
(a, b) = (b, a);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is for copying a decoded block to the output buffer. It handles the pitch and non-4x4 blocks (if necessary).
|
||||
/// </summary>
|
||||
public static void CopyBufferToOutput<TBufferColor, TBufferChannelValue, TOutputColor, TOutputChannelValue>(Span<TBufferColor> buffer, Span<TOutputColor> output, int width, int height, int w, int h, int blockWidth, int blockHeight)
|
||||
where TBufferChannelValue : unmanaged
|
||||
where TBufferColor : unmanaged, IColor<TBufferChannelValue>
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
int hEnd = int.Min(h + blockHeight, height);
|
||||
int wEnd = int.Min(w + blockWidth, width);
|
||||
for (int i = h; i < hEnd; i++)
|
||||
{
|
||||
int outputOffset = i * width;
|
||||
int bufferOffset = (i - h) * blockWidth;
|
||||
for (int j = w; j < wEnd; j++)
|
||||
{
|
||||
output[outputOffset + j] = buffer[bufferOffset + (j - w)].Convert<TBufferColor, TBufferChannelValue, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,32 +6,6 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
{
|
||||
public static class DxtDecoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer of size 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
return DecompressDXT1<ColorBGRA32, byte>(input, width, height, out output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
return DecompressDXT1<ColorBGRA32, byte>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
@@ -64,6 +38,7 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressDXT1<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
@@ -127,32 +102,6 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
return offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT3 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer of size 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
return DecompressDXT3<ColorBGRA32, byte>(input, width, height, out output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT3 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
return DecompressDXT3<ColorBGRA32, byte>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT3 image
|
||||
/// </summary>
|
||||
@@ -185,6 +134,7 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressDXT3<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
@@ -258,32 +208,6 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
return offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT5 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer of size 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
return DecompressDXT5<ColorBGRA32, byte>(input, width, height, out output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT5 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
return DecompressDXT5<ColorBGRA32, byte>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT5 image
|
||||
/// </summary>
|
||||
@@ -316,6 +240,7 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressDXT5<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Etc
|
||||
{
|
||||
public static class EtcDecoder
|
||||
{
|
||||
public static int DecompressETC(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressETC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressETC(input, width, height, output);
|
||||
return DecompressETC<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressETC(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressETC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressETC<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressETC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -19,22 +35,22 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEtc1Block(input.Slice(inputOffset, 8), buf);
|
||||
DecodeEtc1Block(input.Slice(inputOffset, 8), buffer);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,14 +58,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressETC2(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressETC2<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressETC2(input, width, height, output);
|
||||
return DecompressETC2<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressETC2(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressETC2<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressETC2<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressETC2<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -59,22 +88,22 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEtc2Block(input.Slice(inputOffset, 8), buf);
|
||||
DecodeEtc2Block(input.Slice(inputOffset, 8), buffer);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,14 +111,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressETC2A1(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressETC2A1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressETC2A1(input, width, height, output);
|
||||
return DecompressETC2A1<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressETC2A1(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressETC2A1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressETC2A1<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressETC2A1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -99,22 +141,22 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEtc2a1Block(input.Slice(inputOffset, 8), buf);
|
||||
DecodeEtc2a1Block(input.Slice(inputOffset, 8), buffer);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,14 +164,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressETC2A8(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressETC2A8<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressETC2A8(input, width, height, output);
|
||||
return DecompressETC2A8<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressETC2A8(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressETC2A8<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressETC2A8<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressETC2A8<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 16;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -139,23 +194,23 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeEtc2Block(input.Slice(inputOffset + 8, 8), buf);
|
||||
DecodeEtc2a8Block(input.Slice(inputOffset + 0, 8), buf);
|
||||
DecodeEtc2Block(input.Slice(inputOffset + 8, 8), buffer);
|
||||
DecodeEtc2a8Block(input.Slice(inputOffset + 0, 8), buffer);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,14 +218,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressEACRUnsigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressEACRUnsigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressEACRUnsigned(input, width, height, output);
|
||||
return DecompressEACRUnsigned<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressEACRUnsigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressEACRUnsigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressEACRUnsigned<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressEACRUnsigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -180,26 +248,26 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] = 0xFF000000;
|
||||
buffer[i] = ColorRGBA<byte>.Black;
|
||||
}
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset, 8), buf, 2);
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset, 8), buffer, 0);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,14 +275,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressEACRSigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressEACRSigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressEACRSigned(input, width, height, output);
|
||||
return DecompressEACRSigned<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressEACRSigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressEACRSigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressEACRSigned<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressEACRSigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -224,26 +305,26 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] = 0xFF000000;
|
||||
buffer[i] = ColorRGBA<byte>.Black;
|
||||
}
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset, 8), buf, 2);
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset, 8), buffer, 0);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -251,14 +332,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressEACRGUnsigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressEACRGUnsigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressEACRGUnsigned(input, width, height, output);
|
||||
return DecompressEACRGUnsigned<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressEACRGUnsigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressEACRGUnsigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressEACRGUnsigned<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressEACRGUnsigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
|
||||
@@ -268,27 +362,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] = 0xFF000000;
|
||||
buffer[i] = ColorRGBA<byte>.Black;
|
||||
}
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset + 0, 8), buf, 2);
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset + 8, 8), buf, 1);
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset + 0, 8), buffer, 0);
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset + 8, 8), buffer, 1);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,14 +390,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressEACRGSigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressEACRGSigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressEACRGSigned(input, width, height, output);
|
||||
return DecompressEACRGSigned<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressEACRGSigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressEACRGSigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressEACRGSigned<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressEACRGSigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 16;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -313,27 +420,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] = 0xFF000000;
|
||||
buffer[i] = ColorRGBA<byte>.Black;
|
||||
}
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset + 0, 8), buf, 2);
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset + 8, 8), buf, 1);
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset + 0, 8), buffer, 0);
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset + 8, 8), buffer, 1);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -344,7 +451,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
/// <summary>
|
||||
/// Parses an 8-byte Etc1 block from <paramref name="input"/>.
|
||||
/// </summary>
|
||||
private static void DecodeEtc1Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeEtc1Block(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
byte i3 = input[3];
|
||||
ReadOnlySpan<int> code =
|
||||
@@ -393,13 +500,12 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
int cd = code[s];
|
||||
int m = Etc1ModifierTable[cd + index];
|
||||
uint color = ApplicateColor(c, s, m);
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color;
|
||||
output[oi] = ApplicateColor(c, s, m);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeEtc2Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeEtc2Block(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
int j = input[6] << 8 | input[7];
|
||||
int k = input[4] << 8 | input[5];
|
||||
@@ -423,7 +529,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
int ti = input[3] >> 1 & 6 | input[3] & 1;
|
||||
byte d = Etc2DistanceTable[ti];
|
||||
ReadOnlySpan<uint> color_set =
|
||||
ReadOnlySpan<ColorRGBA<byte>> color_set =
|
||||
[
|
||||
ApplicateColorRaw(c, 0),
|
||||
ApplicateColor(c, 1, d),
|
||||
@@ -434,9 +540,8 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
uint color = color_set[index];
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color;
|
||||
output[oi] = color_set[index];
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -464,7 +569,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
++di;
|
||||
}
|
||||
byte d = Etc2DistanceTable[di];
|
||||
ReadOnlySpan<uint> color_set =
|
||||
ReadOnlySpan<ColorRGBA<byte>> color_set =
|
||||
[
|
||||
ApplicateColor(c, 0, d),
|
||||
ApplicateColor(c, 0, -d),
|
||||
@@ -474,9 +579,8 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
uint color = color_set[index];
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color;
|
||||
output[oi] = color_set[index];
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -507,7 +611,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
byte ri = Clamp255((x * (c[1 * 3 + 0] - c[0 * 3 + 0]) + y * (c[2 * 3 + 0] - c[0 * 3 + 0]) + 4 * c[0 * 3 + 0] + 2) >> 2);
|
||||
byte gi = Clamp255((x * (c[1 * 3 + 1] - c[0 * 3 + 1]) + y * (c[2 * 3 + 1] - c[0 * 3 + 1]) + 4 * c[0 * 3 + 1] + 2) >> 2);
|
||||
byte bi = Clamp255((x * (c[1 * 3 + 2] - c[0 * 3 + 2]) + y * (c[2 * 3 + 2] - c[0 * 3 + 2]) + 4 * c[0 * 3 + 2] + 2) >> 2);
|
||||
output[i] = Color(ri, gi, bi, 255);
|
||||
output[i] = new(ri, gi, bi, byte.MaxValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -538,9 +642,8 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
int ci = code[s];
|
||||
int m = Etc1ModifierTable[ci + index];
|
||||
uint color = ApplicateColor(c, s, m);
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color;
|
||||
output[oi] = ApplicateColor(c, s, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -570,15 +673,14 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
int ci = code[s];
|
||||
int m = Etc1ModifierTable[ci + index];
|
||||
uint color = ApplicateColor(c, s, m);
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color;
|
||||
output[oi] = ApplicateColor(c, s, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static void DecodeEtc2a1Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeEtc2a1Block(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
if ((input[3] & 2) != 0)
|
||||
{
|
||||
@@ -591,7 +693,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeEtc2PunchThrowBlock(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeEtc2PunchThrowBlock(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
int j = input[6] << 8 | input[7];
|
||||
int k = input[4] << 8 | input[5];
|
||||
@@ -612,7 +714,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
int ti = input[3] >> 1 & 6 | input[3] & 1;
|
||||
byte d = Etc2DistanceTable[ti];
|
||||
ReadOnlySpan<uint> color_set =
|
||||
ReadOnlySpan<ColorRGBA<byte>> color_set =
|
||||
[
|
||||
ApplicateColorRaw(c, 0),
|
||||
ApplicateColor(c, 1, d),
|
||||
@@ -623,10 +725,8 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
uint color = color_set[index];
|
||||
uint mask = PunchthroughMaskTable[index];
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color & mask;
|
||||
output[oi] = PunchthroughBooleanTable[index] ? color_set[index] : default;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -654,7 +754,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
++di;
|
||||
}
|
||||
byte d = Etc2DistanceTable[di];
|
||||
ReadOnlySpan<uint> color_set =
|
||||
ReadOnlySpan<ColorRGBA<byte>> color_set =
|
||||
[
|
||||
ApplicateColor(c, 0, d),
|
||||
ApplicateColor(c, 0, -d),
|
||||
@@ -664,10 +764,8 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
uint color = color_set[index];
|
||||
uint mask = PunchthroughMaskTable[index];
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color & mask;
|
||||
output[oi] = PunchthroughBooleanTable[index] ? color_set[index] : default;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -698,7 +796,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
byte ri = Clamp255((x * (c[1 * 3 + 0] - c[0 * 3 + 0]) + y * (c[2 * 3 + 0] - c[0 * 3 + 0]) + 4 * c[0 * 3 + 0] + 2) >> 2);
|
||||
byte gi = Clamp255((x * (c[1 * 3 + 1] - c[0 * 3 + 1]) + y * (c[2 * 3 + 1] - c[0 * 3 + 1]) + 4 * c[0 * 3 + 1] + 2) >> 2);
|
||||
byte bi = Clamp255((x * (c[1 * 3 + 2] - c[0 * 3 + 2]) + y * (c[2 * 3 + 2] - c[0 * 3 + 2]) + 4 * c[0 * 3 + 2] + 2) >> 2);
|
||||
output[i] = Color(ri, gi, bi, 255);
|
||||
output[i] = new(ri, gi, bi, byte.MaxValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -725,22 +823,28 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
int s = Etc1SubblockTable[ti + i];
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
int ci = code[s];
|
||||
int m = PunchthroughModifierTable[ci + index];
|
||||
uint color = ApplicateColor(c, s, m);
|
||||
uint mask = PunchthroughMaskTable[index];
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color & mask;
|
||||
if (PunchthroughBooleanTable[index])
|
||||
{
|
||||
int s = Etc1SubblockTable[ti + i];
|
||||
int ci = code[s];
|
||||
int m = PunchthroughModifierTable[ci + index];
|
||||
output[oi] = ApplicateColor(c, s, m);
|
||||
}
|
||||
else
|
||||
{
|
||||
output[oi] = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeEtc2a8Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeEtc2a8Block(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
int @base = input[0];
|
||||
int data1 = input[1];
|
||||
int mul = data1 >> 4;
|
||||
@@ -748,48 +852,50 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(3), @base);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<ColorRGBA<byte>, byte>(output).Slice(3), @base);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int table = data1 & 0xF;
|
||||
ulong l = Get6SwapedBytes(input);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(3), @base, table, mul, l);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<ColorRGBA<byte>, byte>(output).Slice(3), @base, table, mul, l);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeEacUnsignedBlock(ReadOnlySpan<byte> input, Span<uint> output, int channel)
|
||||
private static void DecodeEacUnsignedBlock(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output, int channel)
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
int @base = input[0];
|
||||
int data1 = input[1];
|
||||
int mul = data1 >> 4;
|
||||
if (mul == 0)
|
||||
{
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<ColorRGBA<byte>, byte>(output).Slice(channel), @base);
|
||||
}
|
||||
else
|
||||
{
|
||||
int table = data1 & 0xF;
|
||||
ulong l = Get6SwapedBytes(input);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base, table, mul, l);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<ColorRGBA<byte>, byte>(output).Slice(channel), @base, table, mul, l);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeEacSignedBlock(ReadOnlySpan<byte> input, Span<uint> output, int channel)
|
||||
private static void DecodeEacSignedBlock(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output, int channel)
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
int @base = 127 + unchecked((sbyte)input[0]);
|
||||
int data1 = input[1];
|
||||
int mul = data1 >> 4;
|
||||
if (mul == 0)
|
||||
{
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<ColorRGBA<byte>, byte>(output).Slice(channel), @base);
|
||||
}
|
||||
else
|
||||
{
|
||||
int table = data1 & 0xF;
|
||||
ulong l = Get6SwapedBytes(input);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base, table, mul, l);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<ColorRGBA<byte>, byte>(output).Slice(channel), @base, table, mul, l);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,12 +919,6 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static uint Color(int r, int g, int b, int a)
|
||||
{
|
||||
return unchecked((uint)(r << 16 | g << 8 | b | a << 24));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static byte Clamp255(int n)
|
||||
{
|
||||
@@ -826,21 +926,21 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static uint ApplicateColor(ReadOnlySpan<byte> c, int o, int m)
|
||||
private static ColorRGBA<byte> ApplicateColor(ReadOnlySpan<byte> c, int o, int m)
|
||||
{
|
||||
return Color(Clamp255(c[o * 3 + 0] + m), Clamp255(c[o * 3 + 1] + m), Clamp255(c[o * 3 + 2] + m), 255);
|
||||
return new(Clamp255(c[o * 3 + 0] + m), Clamp255(c[o * 3 + 1] + m), Clamp255(c[o * 3 + 2] + m), byte.MaxValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static uint ApplicateColor(ReadOnlySpan<int> c, int o, int m)
|
||||
private static ColorRGBA<byte> ApplicateColor(ReadOnlySpan<int> c, int o, int m)
|
||||
{
|
||||
return Color(Clamp255(c[o * 3 + 0] + m), Clamp255(c[o * 3 + 1] + m), Clamp255(c[o * 3 + 2] + m), 255);
|
||||
return new(Clamp255(c[o * 3 + 0] + m), Clamp255(c[o * 3 + 1] + m), Clamp255(c[o * 3 + 2] + m), byte.MaxValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static uint ApplicateColorRaw(ReadOnlySpan<byte> c, int o)
|
||||
private static ColorRGBA<byte> ApplicateColorRaw(ReadOnlySpan<byte> c, int o)
|
||||
{
|
||||
return Color(c[o * 3 + 0], c[o * 3 + 1], c[o * 3 + 2], 255);
|
||||
return new(c[o * 3 + 0], c[o * 3 + 1], c[o * 3 + 2], byte.MaxValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
@@ -898,6 +998,6 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
-4, -6, -8, -9, 3, 5, 7, 8,
|
||||
-3, -5, -7, -9, 2, 4, 6, 8,
|
||||
];
|
||||
private static ReadOnlySpan<uint> PunchthroughMaskTable => [0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF];
|
||||
private static ReadOnlySpan<bool> PunchthroughBooleanTable => [true, true, false, true];
|
||||
}
|
||||
}
|
||||
@@ -76,16 +76,16 @@ internal static class BinaryWriterExtensions
|
||||
switch (Unsafe.SizeOf<T>())
|
||||
{
|
||||
case sizeof(byte):
|
||||
writer.Write(Unsafe.As<T, byte>(ref value));
|
||||
writer.Write(Unsafe.BitCast<T, byte>(value));
|
||||
break;
|
||||
case sizeof(ushort):
|
||||
writer.Write(Unsafe.As<T, ushort>(ref value));
|
||||
writer.Write(Unsafe.BitCast<T, ushort>(value));
|
||||
break;
|
||||
case sizeof(uint):
|
||||
writer.Write(Unsafe.As<T, uint>(ref value));
|
||||
writer.Write(Unsafe.BitCast<T, uint>(value));
|
||||
break;
|
||||
case sizeof(ulong):
|
||||
writer.Write(Unsafe.As<T, ulong>(ref value));
|
||||
writer.Write(Unsafe.BitCast<T, ulong>(value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,65 @@
|
||||
//#define DISABLE_TWINDDLING_ROUTINE
|
||||
#define ASSUME_IMAGE_TILING
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
{
|
||||
public static partial class PvrtcDecoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Decompresses PVRTC to RGBA 8888
|
||||
/// Decompresses PVRTC
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">The PVRTC texture data to decompress</param>
|
||||
/// <param name="xDim">X dimension (width) of the texture</param>
|
||||
/// <param name="yDim">Y dimension (height) of the texture</param>
|
||||
/// <param name="output">The decompressed texture data</param>
|
||||
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
|
||||
/// <returns>The number of bytes read from <paramref name="input"/></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static int DecompressPVRTC(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, out byte[] output)
|
||||
public static int DecompressPVRTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[xDim * yDim * sizeof(uint)];
|
||||
return DecompressPVRTC(input, xDim, yDim, do2bitMode, output);
|
||||
output = new byte[xDim * yDim * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressPVRTC<TOutputColor, TOutputChannelValue>(input, xDim, yDim, do2bitMode, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses PVRTC to RGBA 8888
|
||||
/// Decompresses PVRTC
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">The PVRTC texture data to decompress</param>
|
||||
/// <param name="xDim">X dimension (width) of the texture</param>
|
||||
/// <param name="yDim">Y dimension (height) of the texture</param>
|
||||
/// <param name="output">The decompressed texture data</param>
|
||||
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
|
||||
/// <returns>The number of bytes read from <paramref name="input"/></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static int DecompressPVRTC(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, Span<byte> output)
|
||||
public static int DecompressPVRTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressPVRTC<TOutputColor, TOutputChannelValue>(input, xDim, yDim, do2bitMode, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses PVRTC
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">The PVRTC texture data to decompress</param>
|
||||
/// <param name="xDim">X dimension (width) of the texture</param>
|
||||
/// <param name="yDim">Y dimension (height) of the texture</param>
|
||||
/// <param name="output">The decompressed texture data</param>
|
||||
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
|
||||
/// <returns>The number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressPVRTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, xDim * yDim);
|
||||
int xBlockSize = do2bitMode ? BlockX2bpp : BlockX4bpp;
|
||||
// for MBX don't allow the sizes to get too small
|
||||
int blockXDim = Math.Max(2, xDim / xBlockSize);
|
||||
@@ -65,12 +92,12 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
int blockX = (x - xBlockSize / 2);
|
||||
int blockY = (y - BlockYSize / 2);
|
||||
|
||||
blockX = LimitCoord(blockX, xDim) / xBlockSize;
|
||||
blockY = LimitCoord(blockY, yDim) / BlockYSize;
|
||||
blockX = LimitCoordinate(blockX, xDim) / xBlockSize;
|
||||
blockY = LimitCoordinate(blockY, yDim) / BlockYSize;
|
||||
|
||||
// compute the positions of the other 3 blocks
|
||||
int blockXp1 = LimitCoord(blockX + 1, blockXDim);
|
||||
int blockYp1 = LimitCoord(blockY + 1, blockYDim);
|
||||
int blockXp1 = LimitCoordinate(blockX + 1, blockXDim);
|
||||
int blockYp1 = LimitCoordinate(blockY + 1, blockYDim);
|
||||
|
||||
// map to block memory locations
|
||||
uint blocki00 = TwiddleUV((uint)blockYDim, (uint)blockXDim, (uint)blockY, (uint)blockX);
|
||||
@@ -108,12 +135,12 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
InterpolateColours(m_colors[0], m_colors[1], m_colors[2], m_colors[3], 1, do2bitMode, x, y, bSig);
|
||||
GetModulationValue(x, y, do2bitMode, modulationVals, modulationModes, out int mod, out bool doPT);
|
||||
|
||||
// compute the modulated color. Swap red and blue channel
|
||||
int position = (x + y * xDim) << 2;
|
||||
output[position + 0] = (byte)((aSig[2] * 8 + mod * (bSig[2] - aSig[2])) >> 3);
|
||||
output[position + 1] = (byte)((aSig[1] * 8 + mod * (bSig[1] - aSig[1])) >> 3);
|
||||
output[position + 2] = (byte)((aSig[0] * 8 + mod * (bSig[0] - aSig[0])) >> 3);
|
||||
output[position + 3] = doPT ? (byte)0 : (byte)((aSig[3] * 8 + mod * (bSig[3] - aSig[3])) >> 3);
|
||||
// compute the modulated color.
|
||||
byte red = (byte)((aSig[0] * 8 + mod * (bSig[0] - aSig[0])) >> 3);
|
||||
byte green = (byte)((aSig[1] * 8 + mod * (bSig[1] - aSig[1])) >> 3);
|
||||
byte blue = (byte)((aSig[2] * 8 + mod * (bSig[2] - aSig[2])) >> 3);
|
||||
byte alpha = doPT ? (byte)0 : (byte)((aSig[3] * 8 + mod * (bSig[3] - aSig[3])) >> 3);
|
||||
output[x + y * xDim].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(red, green, blue, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,24 +159,10 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
/// <returns>The twiddled offset of the pixel</returns>
|
||||
private static uint TwiddleUV(uint ySize, uint xSize, uint yPos, uint xPos)
|
||||
{
|
||||
#if DEBUG
|
||||
if (yPos >= ySize)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
if (xPos >= xSize)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
if (!BitOperations.IsPow2(ySize))
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
if (!BitOperations.IsPow2(xSize))
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
#endif
|
||||
Debug.Assert(yPos < ySize);
|
||||
Debug.Assert(xPos < xSize);
|
||||
Debug.Assert(BitOperations.IsPow2(ySize));
|
||||
Debug.Assert(BitOperations.IsPow2(xSize));
|
||||
|
||||
uint minDimension;
|
||||
uint maxValue;
|
||||
@@ -164,10 +177,6 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
maxValue = yPos;
|
||||
}
|
||||
|
||||
#if DISABLE_TWINDDLING_ROUTINE
|
||||
// nasty hack to disable twiddling
|
||||
return (yPos * xSize + xPos);
|
||||
#else
|
||||
// step through all the bits in the "minimum" dimension
|
||||
uint srcBitPos = 1;
|
||||
uint dstBitPos = 1;
|
||||
@@ -195,7 +204,6 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
maxValue >>= shiftCount;
|
||||
twiddled |= (maxValue << (2 * shiftCount));
|
||||
return twiddled;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -290,16 +298,11 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// sanity check
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (result[i] >= 256)
|
||||
{
|
||||
throw new Exception("Sanity failed");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Debug.Assert(result[0] < 256);
|
||||
Debug.Assert(result[1] < 256);
|
||||
Debug.Assert(result[2] < 256);
|
||||
Debug.Assert(result[3] < 256);
|
||||
|
||||
// convert from 5554 to 8888, do RGB 5.3 => 8
|
||||
for (int i = 0; i < 3; i++)
|
||||
@@ -308,16 +311,11 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
}
|
||||
result[3] += result[3] >> 4;
|
||||
|
||||
#if DEBUG
|
||||
// 2nd sanity check
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (result[i] >= 256)
|
||||
{
|
||||
throw new Exception("2nd sanity failed");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Debug.Assert(result[0] < 256);
|
||||
Debug.Assert(result[1] < 256);
|
||||
Debug.Assert(result[2] < 256);
|
||||
Debug.Assert(result[3] < 256);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -456,19 +454,20 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Define an expression to either wrap or clamp large or small vals to the legal coordinate range
|
||||
/// Define an expression to either wrap or clamp large or small values to the legal coordinate range.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static int LimitCoord(int value, int size)
|
||||
private static int LimitCoordinate(int value, int size, bool assumeImageTiling = true)
|
||||
{
|
||||
#if ASSUME_IMAGE_TILING
|
||||
// wrap coord
|
||||
return value & (size - 1);
|
||||
#else
|
||||
// clamp
|
||||
return h < x ? h : (x > l ? x : l);
|
||||
return Clamp(Val, 0, Size - 1);
|
||||
#endif
|
||||
if (assumeImageTiling)
|
||||
{
|
||||
// wrap coordinate
|
||||
return value & (size - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// clamp coordinate
|
||||
return int.Clamp(value, 0, size - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -82,7 +82,7 @@ public static class Color
|
||||
{
|
||||
if (typeof(TThis) == typeof(TTarget))
|
||||
{
|
||||
return Unsafe.As<TThis, TTarget>(ref color);
|
||||
return Unsafe.BitCast<TThis, TTarget>(color);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -92,4 +92,30 @@ public static class Color
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ namespace AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
public partial struct ColorA<T> : IColor<ColorA<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
public T A { get; set; }
|
||||
|
||||
public readonly T R
|
||||
{
|
||||
get => NumericConversion.GetMinimumValueSafe<T>();
|
||||
@@ -22,8 +24,6 @@ public partial struct ColorA<T> : IColor<ColorA<T>, T> where T : unmanaged, INum
|
||||
set { }
|
||||
}
|
||||
|
||||
public T A { get; set; }
|
||||
|
||||
public ColorA(T a)
|
||||
{
|
||||
A = a;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
public partial struct ColorARGB<T> : IColor<ColorARGB<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
public T A { get; set; }
|
||||
|
||||
public T R { get; set; }
|
||||
|
||||
public T G { get; set; }
|
||||
|
||||
public T B { get; set; }
|
||||
|
||||
public ColorARGB(T r, T g, T b, T a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out T r, out T g, out T b, out T a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(T r, T g, T b, T a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => true;
|
||||
static bool IColor.HasBlueChannel => true;
|
||||
static bool IColor.HasAlphaChannel => true;
|
||||
static bool IColor.ChannelsAreFullyUtilized => true;
|
||||
static Type IColor.ChannelType => typeof(T);
|
||||
|
||||
public static ColorARGB<T> Black => new(NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
|
||||
public static ColorARGB<T> White => new(NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public partial struct ColorARGB32 : IColor<byte>
|
||||
{
|
||||
public byte A { get; set; }
|
||||
public byte R { get; set; }
|
||||
public byte G { get; set; }
|
||||
public byte B { get; set; }
|
||||
|
||||
public readonly void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public partial struct ColorARGB32 : IColor<byte>
|
||||
{
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => true;
|
||||
static bool IColor.HasBlueChannel => true;
|
||||
static bool IColor.HasAlphaChannel => true;
|
||||
static bool IColor.ChannelsAreFullyUtilized => true;
|
||||
static Type IColor.ChannelType => typeof(byte);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
public partial struct ColorBGR<T> : IColor<ColorBGR<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
public T B { get; set; }
|
||||
|
||||
public T G { get; set; }
|
||||
|
||||
public T R { get; set; }
|
||||
|
||||
public readonly T A
|
||||
{
|
||||
get => NumericConversion.GetMaximumValueSafe<T>();
|
||||
set { }
|
||||
}
|
||||
|
||||
public ColorBGR(T r, T g, T b)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out T r, out T g, out T b, out T a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(T r, T g, T b, T a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => true;
|
||||
static bool IColor.HasBlueChannel => true;
|
||||
static bool IColor.HasAlphaChannel => false;
|
||||
static bool IColor.ChannelsAreFullyUtilized => true;
|
||||
static Type IColor.ChannelType => typeof(T);
|
||||
|
||||
public static ColorBGR<T> Black => new(NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>());
|
||||
public static ColorBGR<T> White => new(NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B} }}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
public partial struct ColorBGRA<T> : IColor<ColorBGRA<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
public T B { get; set; }
|
||||
|
||||
public T G { get; set; }
|
||||
|
||||
public T R { get; set; }
|
||||
|
||||
public T A { get; set; }
|
||||
|
||||
public ColorBGRA(T r, T g, T b, T a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out T r, out T g, out T b, out T a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(T r, T g, T b, T a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => true;
|
||||
static bool IColor.HasBlueChannel => true;
|
||||
static bool IColor.HasAlphaChannel => true;
|
||||
static bool IColor.ChannelsAreFullyUtilized => true;
|
||||
static Type IColor.ChannelType => typeof(T);
|
||||
|
||||
public static ColorBGRA<T> Black => new(NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
|
||||
public static ColorBGRA<T> White => new(NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public partial struct ColorBGRA32 : IColor<byte>
|
||||
{
|
||||
public byte B { get; set; }
|
||||
public byte G { get; set; }
|
||||
public byte R { get; set; }
|
||||
public byte A { get; set; }
|
||||
|
||||
public readonly void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public partial struct ColorBGRA32 : IColor<byte>
|
||||
{
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => true;
|
||||
static bool IColor.HasBlueChannel => true;
|
||||
static bool IColor.HasAlphaChannel => true;
|
||||
static bool IColor.ChannelsAreFullyUtilized => true;
|
||||
static Type IColor.ChannelType => typeof(byte);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public static partial class NumericConversion
|
||||
|
||||
TFrom SignBit = TFrom.One << (Unsafe.SizeOf<TFrom>() * 8 - 1);
|
||||
TFrom converted = (SignBit ^ value);
|
||||
return Unsafe.As<TFrom, TTo>(ref converted);
|
||||
return Unsafe.BitCast<TFrom, TTo>(converted);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
@@ -56,6 +56,6 @@ public static partial class NumericConversion
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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)]
|
||||
public static int Convert<TSourceColor, TSourceChannel, TDestinationColor, TDestinationChannel>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TSourceChannel : unmanaged
|
||||
@@ -503,6 +20,7 @@ namespace AssetRipper.TextureDecoder.Rgb
|
||||
where TDestinationChannel : unmanaged
|
||||
where TDestinationColor : unmanaged, IColor<TDestinationChannel>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ReadOnlySpan<TSourceColor> sourceSpan = MemoryMarshal.Cast<byte, TSourceColor>(input).Slice(0, width * height);
|
||||
Span<TDestinationColor> destinationSpan = MemoryMarshal.Cast<byte, TDestinationColor>(output).Slice(0, width * height);
|
||||
Convert<TSourceColor, TSourceChannel, TDestinationColor, TDestinationChannel>(sourceSpan, destinationSpan);
|
||||
@@ -516,34 +34,55 @@ namespace AssetRipper.TextureDecoder.Rgb
|
||||
where TDestinationChannel : unmanaged
|
||||
where TDestinationColor : unmanaged, IColor<TDestinationChannel>
|
||||
{
|
||||
for (int i = 0; i < sourceSpan.Length; i++)
|
||||
if (sourceSpan.Length is 0)
|
||||
{
|
||||
destinationSpan[i] = sourceSpan[i].Convert<TSourceColor, TSourceChannel, TDestinationColor, TDestinationChannel>();
|
||||
// Do nothing if the source span is empty
|
||||
}
|
||||
else if (destinationSpan.Length < sourceSpan.Length)
|
||||
{
|
||||
ThrowDestinationSpanNotLargeEnough();
|
||||
}
|
||||
else if (typeof(TSourceColor) != typeof(TDestinationColor))
|
||||
{
|
||||
for (int i = 0; i < sourceSpan.Length; i++)
|
||||
{
|
||||
destinationSpan[i] = sourceSpan[i].Convert<TSourceColor, TSourceChannel, TDestinationColor, TDestinationChannel>();
|
||||
}
|
||||
}
|
||||
else if (Unsafe.AreSame(in sourceSpan[0], ref Unsafe.As<TDestinationColor, TSourceColor>(ref destinationSpan[0])))
|
||||
{
|
||||
// Do nothing because the source and destination point to the same memory
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note: This assumes that the source and destination will never overlap.
|
||||
sourceSpan.CopyTo(MemoryMarshal.Cast<TDestinationColor, TSourceColor>(destinationSpan));
|
||||
}
|
||||
|
||||
static void ThrowDestinationSpanNotLargeEnough()
|
||||
{
|
||||
throw new ArgumentException("Destination span is not large enough to hold the converted data.", nameof(destinationSpan));
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static float ToHalf(ReadOnlySpan<byte> input, int offset)
|
||||
public static void SRgbToLinear<TColor, TChannel>(Span<TColor> span)
|
||||
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)]
|
||||
private static float ToSingle(ReadOnlySpan<byte> input, int offset)
|
||||
public static void LinearToSRgb<TColor, TChannel>(Span<TColor> span)
|
||||
where TChannel : unmanaged
|
||||
where TColor : unmanaged, IColor<TChannel>
|
||||
{
|
||||
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);
|
||||
for (int i = 0; i < span.Length; i++)
|
||||
{
|
||||
span[i] = span[i].LinearToSRgb<TColor, TChannel>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,5 +19,13 @@
|
||||
throw new InvalidOperationException($"Not enough space. {spaceRequired} is required, but only {spaceAvailable} is available");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ThrowIfNotLittleEndian()
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,9 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Yuy2
|
||||
{
|
||||
public static class Yuy2Decoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Decompress a YUY2 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer of size 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressYUY2(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
return DecompressYUY2<ColorBGRA32, byte>(input, width, height, out output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a YUY2 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressYUY2(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
return DecompressYUY2<ColorBGRA32, byte>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a YUY2 image
|
||||
/// </summary>
|
||||
@@ -63,6 +36,7 @@ namespace AssetRipper.TextureDecoder.Yuy2
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressYUY2<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsAotCompatible>true</IsAotCompatible>
|
||||
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||
|
||||
<Version>2.2.1</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<VersionPrefix>2.6.1</VersionPrefix>
|
||||
<Copyright>Copyright © ds5678</Copyright>
|
||||
<Authors>ds5678</Authors>
|
||||
<Company>AssetRipper</Company>
|
||||
|
||||
@@ -8,17 +8,18 @@ internal class Program
|
||||
{
|
||||
int originalBitCount = int.Parse(args[0]);
|
||||
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($"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;
|
||||
b = default;
|
||||
c = default;
|
||||
float bestError = float.MaxValue;//Sum of squared errors
|
||||
double bestError = double.MaxValue;//Sum of squared errors
|
||||
uint originalMaxValue = GetIntegerMaxValue(originalBitCount);
|
||||
uint newMaxValue = GetIntegerMaxValue(newBitCount);
|
||||
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.
|
||||
//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);
|
||||
// We are limited to a 32-bit result, so we cannot shift left so much that we exceed 32 bits.
|
||||
int maxc = 32 - newBitCount;
|
||||
|
||||
for (int ic = minc; ic <= maxc; ic++)
|
||||
{
|
||||
Console.WriteLine(ic);
|
||||
|
||||
//a: 2 ^ (c + bitDiff) to 2 ^ (c + bitDiff + 1)
|
||||
uint mina = Pow2(ic + bitDiff);
|
||||
uint maxa = Pow2(ic + bitDiff + 1);
|
||||
//a: 2^c * newMaxValue / originalMaxValue rounded to nearest integer
|
||||
double ia_exact = (double)Pow2(ic) * newMaxValue / originalMaxValue;
|
||||
|
||||
//b: 0 to 2^c - 1
|
||||
//Any bigger and it will cause 0 not to correspond with 0.
|
||||
uint minb = 0;
|
||||
uint maxb = Pow2(ic) - 1;
|
||||
ReadOnlySpan<uint> ia_values = ia_exact < 1 ? [(uint)double.Ceiling(ia_exact)] : [(uint)double.Floor(ia_exact), (uint)double.Ceiling(ia_exact)];
|
||||
|
||||
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;
|
||||
for (uint x = 1; x < originalMaxValue; x++)
|
||||
{
|
||||
uint y = ConvertEstimate(x, ia, ib, ic);
|
||||
float yExact = (float)x * newMaxValue / originalMaxValue;
|
||||
float diff = yExact - y;
|
||||
error += diff * diff;
|
||||
}
|
||||
if (error < bestError)
|
||||
{
|
||||
a = ia;
|
||||
b = ib;
|
||||
c = ic;
|
||||
if (error == 0)//Huge performance savings for when newBitCount / originalBitCount has no remainder.
|
||||
{
|
||||
return;
|
||||
}
|
||||
bestError = error;
|
||||
}
|
||||
current_b_high = b2;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_b_low = b1;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint ib = current_b_low; ib <= current_b_high; ib++)
|
||||
{
|
||||
double error = CalculateMeanSquaredError(ia, ib, ic, originalMaxValue, newMaxValue, out bool anyIncorrect);
|
||||
if (error < bestError)
|
||||
{
|
||||
bestError = error;
|
||||
a = ia;
|
||||
b = ib;
|
||||
c = ic;
|
||||
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)]
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32112.339
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3AAF94E5-9A19-461C-923F-BBC603C630BB}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
.gitignore = .gitignore
|
||||
Directory.Build.props = Directory.Build.props
|
||||
LICENSE = LICENSE
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{226FED9B-5CD8-4FD0-8E77-660F542D7508}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.github\workflows\build.yml = .github\workflows\build.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Licenses", "Licenses", "{0BFF6817-43C8-43EE-8EE8-1A03B7ECEA1A}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Licenses\bcdec.md = Licenses\bcdec.md
|
||||
Licenses\uTinyRipper.md = Licenses\uTinyRipper.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.TextureDecoder.Tests", "AssetRipper.TextureDecoder.Tests\AssetRipper.TextureDecoder.Tests.csproj", "{30E8E8F4-2CE4-4435-A49F-C5AF91D58FFE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.TextureDecoder", "AssetRipper.TextureDecoder\AssetRipper.TextureDecoder.csproj", "{E5A6E093-436C-4455-A597-3E357D99C4D9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.TextureDecoder.TestGenerator", "AssetRipper.TextureDecoder.TestGenerator\AssetRipper.TextureDecoder.TestGenerator.csproj", "{0074908C-35A8-4C71-8F74-A83A4095ACEA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.TextureDecoder.ConsoleApp", "AssetRipper.TextureDecoder.ConsoleApp\AssetRipper.TextureDecoder.ConsoleApp.csproj", "{FFC8EEF8-8BF1-4B65-ADDA-A949EDDF4CB4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.TextureDecoder.Benchmarks", "AssetRipper.TextureDecoder.Benchmarks\AssetRipper.TextureDecoder.Benchmarks.csproj", "{F85804F4-4864-412E-BDC6-8B6E2B5646A7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.TextureDecoder.ColorGenerator", "AssetRipper.TextureDecoder.ColorGenerator\AssetRipper.TextureDecoder.ColorGenerator.csproj", "{E9ED1120-1CEA-433E-9BFF-C4B20A34CC7B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegerConversion.ConstantEstimation", "IntegerConversion.ConstantEstimation\IntegerConversion.ConstantEstimation.csproj", "{9897C52D-2F38-405A-AB85-B06383FDF6E0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.TextureDecoder.SourceGeneration.Common", "AssetRipper.TextureDecoder.SourceGeneration.Common\AssetRipper.TextureDecoder.SourceGeneration.Common.csproj", "{336CF443-373C-41B3-8E7C-881672E9875E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.TextureDecoder.Paths", "AssetRipper.TextureDecoder.Paths\AssetRipper.TextureDecoder.Paths.csproj", "{48731523-A672-4D39-A49E-2C4C253B4491}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{30E8E8F4-2CE4-4435-A49F-C5AF91D58FFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{30E8E8F4-2CE4-4435-A49F-C5AF91D58FFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{30E8E8F4-2CE4-4435-A49F-C5AF91D58FFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{30E8E8F4-2CE4-4435-A49F-C5AF91D58FFE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E5A6E093-436C-4455-A597-3E357D99C4D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E5A6E093-436C-4455-A597-3E357D99C4D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E5A6E093-436C-4455-A597-3E357D99C4D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E5A6E093-436C-4455-A597-3E357D99C4D9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0074908C-35A8-4C71-8F74-A83A4095ACEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0074908C-35A8-4C71-8F74-A83A4095ACEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0074908C-35A8-4C71-8F74-A83A4095ACEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0074908C-35A8-4C71-8F74-A83A4095ACEA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FFC8EEF8-8BF1-4B65-ADDA-A949EDDF4CB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FFC8EEF8-8BF1-4B65-ADDA-A949EDDF4CB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FFC8EEF8-8BF1-4B65-ADDA-A949EDDF4CB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FFC8EEF8-8BF1-4B65-ADDA-A949EDDF4CB4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F85804F4-4864-412E-BDC6-8B6E2B5646A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F85804F4-4864-412E-BDC6-8B6E2B5646A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F85804F4-4864-412E-BDC6-8B6E2B5646A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F85804F4-4864-412E-BDC6-8B6E2B5646A7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E9ED1120-1CEA-433E-9BFF-C4B20A34CC7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E9ED1120-1CEA-433E-9BFF-C4B20A34CC7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E9ED1120-1CEA-433E-9BFF-C4B20A34CC7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E9ED1120-1CEA-433E-9BFF-C4B20A34CC7B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9897C52D-2F38-405A-AB85-B06383FDF6E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9897C52D-2F38-405A-AB85-B06383FDF6E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9897C52D-2F38-405A-AB85-B06383FDF6E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9897C52D-2F38-405A-AB85-B06383FDF6E0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{336CF443-373C-41B3-8E7C-881672E9875E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{336CF443-373C-41B3-8E7C-881672E9875E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{336CF443-373C-41B3-8E7C-881672E9875E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{336CF443-373C-41B3-8E7C-881672E9875E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{48731523-A672-4D39-A49E-2C4C253B4491}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{48731523-A672-4D39-A49E-2C4C253B4491}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{48731523-A672-4D39-A49E-2C4C253B4491}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{48731523-A672-4D39-A49E-2C4C253B4491}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{226FED9B-5CD8-4FD0-8E77-660F542D7508} = {3AAF94E5-9A19-461C-923F-BBC603C630BB}
|
||||
{0BFF6817-43C8-43EE-8EE8-1A03B7ECEA1A} = {3AAF94E5-9A19-461C-923F-BBC603C630BB}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D4E786DF-F6DD-407C-B23E-854CF00B7801}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Reference in new issue
Block a user