mirror of
https://github.com/AssetRipper/AssetRipper.TextureDecoder.git
synced 2026-11-13 04:26:30 -10:00
Compare commits
107
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 | ||
|
|
19b2469595 | ||
|
|
c5a2382733 | ||
|
|
9ce8311e1b | ||
|
|
e39b439ccd | ||
|
|
f2da43bcbb | ||
|
|
128eb58864 | ||
|
|
9f7a61172a | ||
|
|
93c177232f | ||
|
|
8b62957cdb | ||
|
|
95ecf5b1ac | ||
|
|
f8d17ad290 | ||
|
|
f2772dcb83 | ||
|
|
1e363e355e | ||
|
|
8e82e702a7 | ||
|
|
1957784de4 | ||
|
|
1bf5f70ead | ||
|
|
2bb946b26f | ||
|
|
eed528cc68 | ||
|
|
c6d3236770 | ||
|
|
34d8310764 | ||
|
|
e7b9db0dd6 | ||
|
|
1e7ef0c44c | ||
|
|
0f2a6fdec1 | ||
|
|
058a14953f | ||
|
|
94f1a9ab8e | ||
|
|
5953a2baf7 | ||
|
|
4b398e6c94 | ||
|
|
a25a42dbd4 | ||
|
|
e9674d1c39 | ||
|
|
05eaeb81dc | ||
|
|
76a8b2f837 | ||
|
|
c113649b6e | ||
|
|
07002c77ee | ||
|
|
bed45ee60e | ||
|
|
d8918771a9 | ||
|
|
5dba4cdb19 | ||
|
|
b1803e8a35 | ||
|
|
5ec29c6a82 | ||
|
|
c6a1240499 | ||
|
|
12c6750a88 | ||
|
|
5dacd489d0 | ||
|
|
509a567aa5 | ||
|
|
1f1dd55e85 | ||
|
|
5e28554f00 | ||
|
|
d4cd1d5560 | ||
|
|
9ae8296532 | ||
|
|
637a53fc6f | ||
|
|
53f177cae3 | ||
|
|
c57303ff6b | ||
|
|
bd834212ad | ||
|
|
f0bdada27c | ||
|
|
4a386d57d7 | ||
|
|
d96d8e6c35 | ||
|
|
c16bec4431 | ||
|
|
548d5e8aec | ||
|
|
9e21272b2b | ||
|
|
df1ab3c1e4 | ||
|
|
7c30eb44ee | ||
|
|
e32f934043 |
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@v3.2.0
|
||||
uses: actions/setup-dotnet@v5.1.0
|
||||
with:
|
||||
dotnet-version: 7.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,18 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.8" />
|
||||
</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
-4
@@ -2,13 +2,10 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.1.0" />
|
||||
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -42,8 +42,11 @@ internal static class NumericConversionGenerator
|
||||
{
|
||||
foreach ((CSharpPrimitives.Data unsignedType, CSharpPrimitives.Data signedType) in GetIntegerTypes())
|
||||
{
|
||||
WriteMethod(writer, unsignedType, signedType, unsignedType);
|
||||
WriteMethod(writer, signedType, unsignedType, unsignedType);
|
||||
if (!unsignedType.HasPointerSize(out _, out _))
|
||||
{
|
||||
WriteMethod(writer, unsignedType, signedType, unsignedType);
|
||||
WriteMethod(writer, signedType, unsignedType, unsignedType);
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteMethod(IndentedTextWriter writer, CSharpPrimitives.Data parameterType, CSharpPrimitives.Data returnType, CSharpPrimitives.Data unsignedType)
|
||||
@@ -79,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");
|
||||
@@ -105,10 +108,20 @@ internal static class NumericConversionGenerator
|
||||
writer.WriteLine($"private static TTo {methodName}<TTo>({from.LangName} value) where TTo : unmanaged");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
if (from.IsSignedInteger(out CSharpPrimitives.Data? unsignedFromData))
|
||||
if (from.HasPointerSize(out CSharpPrimitives.Data? bit32From, out CSharpPrimitives.Data? bit64From))
|
||||
{
|
||||
writer.WriteLine($"{unsignedFromData.LangName} unsigned = {ChangeSign}(value);");
|
||||
writer.WriteLine($"return {ConvertMethodName(unsignedFromData.Type)}<TTo>(unsigned);");
|
||||
using (new If(writer, "IntPtr.Size == sizeof(int)"))
|
||||
{
|
||||
writer.WriteLine($"return {ConvertMethodName(bit32From.Type)}<TTo>(({bit32From.LangName})value);");
|
||||
}
|
||||
using (new Else(writer))
|
||||
{
|
||||
writer.WriteLine($"return {ConvertMethodName(bit64From.Type)}<TTo>(({bit64From.LangName})value);");
|
||||
}
|
||||
}
|
||||
else if (from.IsSignedInteger(out CSharpPrimitives.Data? unsignedFrom))
|
||||
{
|
||||
writer.WriteLine($"return {ConvertMethodName(unsignedFrom.Type)}<TTo>({ChangeSign}(value));");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -120,27 +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.BitCast<{to.LangName}, TTo>(converted);");
|
||||
}
|
||||
using (new Else(writer))
|
||||
{
|
||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName}){methodName}<{bit64To.LangName}>(value);");
|
||||
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
|
||||
{
|
||||
@@ -149,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);");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,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
|
||||
@@ -188,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
|
||||
@@ -199,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
|
||||
{
|
||||
@@ -210,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);");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,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,69 +1,85 @@
|
||||
using AssetRipper.Text.SourceGeneration;
|
||||
using AssetRipper.TextureDecoder.SourceGeneration.Common;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.ColorGenerator;
|
||||
|
||||
internal static partial class Program
|
||||
{
|
||||
private const string AttributeNamespace = "AssetRipper.TextureDecoder.Attributes";
|
||||
private const string OutputNamespace = "AssetRipper.TextureDecoder.Rgb.Formats";
|
||||
private const string OutputFolder = "../../../../AssetRipper.TextureDecoder/Rgb/Formats/";
|
||||
private const string RgbNamespace = "AssetRipper.TextureDecoder.Rgb";
|
||||
private const string RgbFolder = "../../../../AssetRipper.TextureDecoder/Rgb/";
|
||||
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);
|
||||
}
|
||||
for (int i = 1; i <= 4; i++)
|
||||
{
|
||||
WriteSuperGenericColor(i);
|
||||
}
|
||||
NumericConversionGenerator.Run();
|
||||
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);
|
||||
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(OutputFolder, name);
|
||||
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(FormatsFolder, name);
|
||||
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(OutputFolder, name);
|
||||
WriteGenericColor(writer, name, hasRed, hasGreen, hasBlue, hasAlpha);
|
||||
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(FormatsFolder, name);
|
||||
WriteGenericColor(writer, name);
|
||||
}
|
||||
|
||||
private static void WriteSuperGenericColor(int channelCount)
|
||||
{
|
||||
string name = $"Color`{channelCount}";
|
||||
Console.WriteLine(name);
|
||||
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(RgbFolder, name);
|
||||
WriteSuperGenericColor(writer, channelCount);
|
||||
}
|
||||
|
||||
private static void WriteCustomColor(IndentedTextWriter writer, string name, Type type, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized)
|
||||
@@ -71,12 +87,12 @@ internal static partial class Program
|
||||
string typeName = CSharpPrimitives.Dictionary[type].LangName;
|
||||
writer.WriteGeneratedCodeWarning();
|
||||
writer.WriteLine();
|
||||
using (new Namespace(writer, OutputNamespace))
|
||||
using (new Namespace(writer, FormatsNamespace))
|
||||
{
|
||||
writer.WriteLine($"public partial struct {name} : IColor<{typeName}>");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
WriteColorBaseStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, fullyUtilized, typeName);
|
||||
WriteNonGenericStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, fullyUtilized, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteToString(writer, hasRed, hasGreen, hasBlue, hasAlpha);
|
||||
}
|
||||
@@ -127,53 +143,355 @@ internal static partial class Program
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteColorBaseStaticProperties(IndentedTextWriter writer, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized, string typeName)
|
||||
private static void WriteNonGenericStaticProperties(IndentedTextWriter writer, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized, string typeName)
|
||||
{
|
||||
writer.WriteLine($"static bool IColorBase.HasRedChannel => {hasRed.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColorBase.HasGreenChannel => {hasGreen.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColorBase.HasBlueChannel => {hasBlue.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColorBase.HasAlphaChannel => {hasAlpha.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColorBase.ChannelsAreFullyUtilized => {fullyUtilized.ToLowerString()};");
|
||||
writer.WriteLine($"static Type IColorBase.ChannelType => typeof({typeName});");
|
||||
WriteNonGenericStaticProperties(writer, hasRed.ToLowerString(), hasGreen.ToLowerString(), hasBlue.ToLowerString(), hasAlpha.ToLowerString(), fullyUtilized.ToLowerString(), typeName);
|
||||
}
|
||||
|
||||
private static void WriteGenericColor(IndentedTextWriter writer, string name, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha)
|
||||
private static void WriteNonGenericStaticProperties(IndentedTextWriter writer, string hasRed, string hasGreen, string hasBlue, string hasAlpha, string fullyUtilized, string typeName)
|
||||
{
|
||||
writer.WriteLine($"static bool IColor.HasRedChannel => {hasRed};");
|
||||
writer.WriteLine($"static bool IColor.HasGreenChannel => {hasGreen};");
|
||||
writer.WriteLine($"static bool IColor.HasBlueChannel => {hasBlue};");
|
||||
writer.WriteLine($"static bool IColor.HasAlphaChannel => {hasAlpha};");
|
||||
writer.WriteLine($"static bool IColor.ChannelsAreFullyUtilized => {fullyUtilized};");
|
||||
writer.WriteLine($"static Type IColor.ChannelType => typeof({typeName});");
|
||||
}
|
||||
|
||||
private static void WriteBlackWhiteStaticProperties(IndentedTextWriter writer, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, string colorTypeName, string channelTypeName)
|
||||
{
|
||||
string minValue = $"NumericConversion.GetMinimumValueSafe<{channelTypeName}>()";
|
||||
string maxValue = $"NumericConversion.GetMaximumValueSafe<{channelTypeName}>()";
|
||||
|
||||
//Black
|
||||
{
|
||||
writer.Write($"public static {colorTypeName}<{channelTypeName}> Black => new(");
|
||||
int rgbCount = CountRGB(hasRed, hasGreen, hasBlue);
|
||||
for (int i = 0; i < rgbCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
writer.Write(minValue);
|
||||
}
|
||||
if (hasAlpha)
|
||||
{
|
||||
if (rgbCount != 0)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
writer.Write(maxValue);
|
||||
}
|
||||
writer.WriteLine(");");
|
||||
}
|
||||
|
||||
//White
|
||||
if (hasRed || hasGreen || hasBlue)
|
||||
{
|
||||
writer.Write($"public static {colorTypeName}<{channelTypeName}> White => new(");
|
||||
int rgbaCount = CountRGBA(hasRed, hasGreen, hasBlue, hasAlpha);
|
||||
for (int i = 0; i < rgbaCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
writer.Write(maxValue);
|
||||
}
|
||||
writer.WriteLine(");");
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteLine($"static {colorTypeName}<{channelTypeName}> IColor<{colorTypeName}<{channelTypeName}>, {channelTypeName}>.White => throw new NotSupportedException();");
|
||||
}
|
||||
|
||||
static int CountRGB(bool hasRed, bool hasGreen, bool hasBlue)
|
||||
{
|
||||
return (hasRed ? 1 : 0) + (hasGreen ? 1 : 0) + (hasBlue ? 1 : 0);
|
||||
}
|
||||
|
||||
static int CountRGBA(bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha)
|
||||
{
|
||||
return CountRGB(hasRed, hasGreen, hasBlue) + (hasAlpha ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteGenericColor(IndentedTextWriter writer, string name)
|
||||
{
|
||||
const string typeName = "T";
|
||||
const string minValue = $"NumericConversion.GetMinimumValueSafe<T>()";
|
||||
const string maxValue = $"NumericConversion.GetMaximumValueSafe<T>()";
|
||||
const string minValue = "NumericConversion.GetMinimumValueSafe<T>()";
|
||||
const string maxValue = "NumericConversion.GetMaximumValueSafe<T>()";
|
||||
|
||||
writer.WriteGeneratedCodeWarning();
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteFileScopedNamespace(OutputNamespace);
|
||||
writer.WriteFileScopedNamespace(FormatsNamespace);
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
string constraints = hasRed && hasGreen && hasBlue && hasAlpha
|
||||
? "unmanaged"
|
||||
: "unmanaged, INumberBase<T>, IMinMaxValue<T>";
|
||||
writer.WriteLine($"public partial struct {name}<T> : IColor<{typeName}> where T : {constraints}");
|
||||
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();
|
||||
WriteGetChannels(writer, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteSetChannels(writer, typeName, hasRed, hasGreen, hasBlue, hasAlpha);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteColorBaseStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, true, typeName);
|
||||
WriteNonGenericStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, true, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteBlackWhiteStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, name, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteToString(writer, hasRed, hasGreen, hasBlue, hasAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteSuperGenericColor(IndentedTextWriter writer, int channelCount)
|
||||
{
|
||||
const string typeName = "TChannelValue";
|
||||
const string minValue = $"NumericConversion.GetMinimumValueSafe<{typeName}>()";
|
||||
const string maxValue = $"NumericConversion.GetMaximumValueSafe<{typeName}>()";
|
||||
|
||||
string[] channelTypeParameters = channelCount switch
|
||||
{
|
||||
1 => ["TChannel"],
|
||||
2 => ["TChannel1", "TChannel2"],
|
||||
3 => ["TChannel1", "TChannel2", "TChannel3"],
|
||||
4 => ["TChannel1", "TChannel2", "TChannel3", "TChannel4"],
|
||||
_ => throw new(),
|
||||
};
|
||||
|
||||
string[] fieldNames = channelCount switch
|
||||
{
|
||||
1 => ["value"],
|
||||
2 => ["value1", "value2"],
|
||||
3 => ["value1", "value2", "value3"],
|
||||
4 => ["value1", "value2", "value3", "value4"],
|
||||
_ => throw new(),
|
||||
};
|
||||
|
||||
|
||||
|
||||
writer.WriteGeneratedCodeWarning();
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteUsing("AssetRipper.TextureDecoder.Rgb.Channels");
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteFileScopedNamespace("AssetRipper.TextureDecoder.Rgb");
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
string structName;
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.Append("Color<");
|
||||
sb.Append(typeName);
|
||||
for (int i = 0; i < channelTypeParameters.Length; i++)
|
||||
{
|
||||
sb.Append(", ");
|
||||
sb.Append(channelTypeParameters[i]);
|
||||
}
|
||||
sb.Append('>');
|
||||
structName = sb.ToString();
|
||||
}
|
||||
|
||||
writer.WriteLine($"public partial struct {structName} : IColor<{structName}, {typeName}>");
|
||||
using (new Indented(writer))
|
||||
{
|
||||
writer.WriteLine($"where {typeName} : unmanaged, INumberBase<{typeName}>, IMinMaxValue<{typeName}>");
|
||||
foreach (string channel in channelTypeParameters)
|
||||
{
|
||||
writer.WriteLine($"where {channel} : IChannel");
|
||||
}
|
||||
}
|
||||
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
foreach (string fieldName in fieldNames)
|
||||
{
|
||||
writer.WriteLine($"private {typeName} {fieldName};");
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
WriteProperty(writer, 'R', "Red", channelTypeParameters, fieldNames, minValue, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteProperty(writer, 'G', "Green", channelTypeParameters, fieldNames, minValue, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteProperty(writer, 'B', "Blue", channelTypeParameters, fieldNames, minValue, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteProperty(writer, 'A', "Alpha", channelTypeParameters, fieldNames, maxValue, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.Write("public Color(");
|
||||
for (int i = 0; i < fieldNames.Length; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
|
||||
writer.Write(typeName);
|
||||
writer.Write(' ');
|
||||
writer.Write(fieldNames[i]);
|
||||
}
|
||||
writer.WriteLine(')');
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
foreach (string fieldName in fieldNames)
|
||||
{
|
||||
writer.WriteLine($"this.{fieldName} = {fieldName};");
|
||||
}
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
WriteGetChannels(writer, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteSetChannels(writer, typeName, true, true, true, true);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteNonGenericStaticProperties(
|
||||
writer,
|
||||
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsRed")),
|
||||
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsGreen")),
|
||||
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsBlue")),
|
||||
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsAlpha")),
|
||||
string.Join(" && ", channelTypeParameters.Select(c => $"{c}.FullyUtilized")),
|
||||
typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteLine($"public static {structName} Black");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
writer.WriteLine($"get => new({string.Join(", ", channelTypeParameters.Select(c => $"{c}.GetBlack<{typeName}>()"))});");
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteLine($"public static {structName} White");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
writer.WriteLine($"get => new({string.Join(", ", channelTypeParameters.Select(c => $"{c}.GetWhite<{typeName}>()"))});");
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
WriteToString(writer, true, true, true, true);
|
||||
}
|
||||
|
||||
static void WriteProperty(IndentedTextWriter writer, char property, string channel, string[] channels, string[] fields, string defaultValue, string typeName)
|
||||
{
|
||||
writer.WriteLine($"public {typeName} {property}");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
writer.WriteLine("readonly get");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
If.Write(
|
||||
writer,
|
||||
Enumerable.Range(0, channels.Length).Select(i => new ValueTuple<string, Action<IndentedTextWriter>?>($"{channels[i]}.Is{channel}", (writer) =>
|
||||
{
|
||||
writer.WriteLine($"return {channels[i]}.Get{channel}({fields[i]});");
|
||||
})),
|
||||
(writer) =>
|
||||
{
|
||||
writer.WriteLine($"return {defaultValue};");
|
||||
});
|
||||
}
|
||||
writer.WriteLine("set");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
for (int i = 0; i < channels.Length; i++)
|
||||
{
|
||||
writer.WriteLine($"Channel.SetIf{channel}<{channels[i]}, {typeName}>(ref {fields[i]}, value);");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteConstructor(IndentedTextWriter writer, string declaringTypeName, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, string channelTypeName)
|
||||
{
|
||||
writer.Write($"public {declaringTypeName}(");
|
||||
if (hasRed)
|
||||
{
|
||||
writer.Write($"{channelTypeName} r");
|
||||
if (hasGreen || hasBlue || hasAlpha)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
}
|
||||
if (hasGreen)
|
||||
{
|
||||
writer.Write($"{channelTypeName} g");
|
||||
if (hasBlue || hasAlpha)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
}
|
||||
if (hasBlue)
|
||||
{
|
||||
writer.Write($"{channelTypeName} b");
|
||||
if (hasAlpha)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
}
|
||||
if (hasAlpha)
|
||||
{
|
||||
writer.Write($"{channelTypeName} a");
|
||||
}
|
||||
writer.WriteLine(")");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
if (hasRed)
|
||||
{
|
||||
writer.WriteLine("R = r;");
|
||||
}
|
||||
if (hasGreen)
|
||||
{
|
||||
writer.WriteLine("G = g;");
|
||||
}
|
||||
if (hasBlue)
|
||||
{
|
||||
writer.WriteLine("B = b;");
|
||||
}
|
||||
if (hasAlpha)
|
||||
{
|
||||
writer.WriteLine("A = a;");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteGetChannels(IndentedTextWriter writer, string typeName)
|
||||
{
|
||||
writer.WriteLine($"public readonly void GetChannels(out {typeName} r, out {typeName} g, out {typeName} b, out {typeName} a)");
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<PublishAot>true</PublishAot>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using AssetRipper.TextureDecoder.Exr;
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using StbImageWriteSharp;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -54,6 +56,12 @@ public readonly struct DirectBitmap<TColor, TColorArg>
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveAsExr(string path)
|
||||
{
|
||||
using Stream stream = File.OpenWrite(path);
|
||||
ExrWriter.Write<TColor, TColorArg>(stream, Width, Height, Pixels);
|
||||
}
|
||||
|
||||
public void SaveAsHdr(string path)
|
||||
{
|
||||
GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components);
|
||||
@@ -96,24 +104,34 @@ public readonly struct DirectBitmap<TColor, TColorArg>
|
||||
|
||||
private void GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components)
|
||||
{
|
||||
if (typeof(TColor) == typeof(ColorRGBA<byte>))
|
||||
if (typeof(TColor) == typeof(Color<byte, R, G, B, A>) || typeof(TColor) == typeof(ColorRGBA<byte>))
|
||||
{
|
||||
data = Data;
|
||||
components = ColorComponents.RedGreenBlueAlpha;
|
||||
}
|
||||
else if (typeof(TColor) == typeof(ColorRGB<byte>))
|
||||
else if (typeof(TColor) == typeof(Color<byte, R, G, B>) || typeof(TColor) == typeof(ColorRGB<byte>))
|
||||
{
|
||||
data = Data;
|
||||
components = ColorComponents.RedGreenBlue;
|
||||
}
|
||||
else if (typeof(TColor) == typeof(Color<byte, Gray, A>))
|
||||
{
|
||||
data = Data;
|
||||
components = ColorComponents.GreyAlpha;
|
||||
}
|
||||
else if (typeof(TColor) == typeof(Color<byte, Gray>))
|
||||
{
|
||||
data = Data;
|
||||
components = ColorComponents.Grey;
|
||||
}
|
||||
else if (TColor.HasAlphaChannel)
|
||||
{
|
||||
RgbConverter.Convert<TColor, TColorArg, ColorRGBA<byte>, byte>(Bits, Width, Height, out data);
|
||||
RgbConverter.Convert<TColor, TColorArg, Color<byte, R, G, B, A>, byte>(Bits, Width, Height, out data);
|
||||
components = ColorComponents.RedGreenBlueAlpha;
|
||||
}
|
||||
else
|
||||
{
|
||||
RgbConverter.Convert<TColor, TColorArg, ColorRGB<byte>, byte>(Bits, Width, Height, out data);
|
||||
RgbConverter.Convert<TColor, TColorArg, Color<byte, R, G, B>, byte>(Bits, Width, Height, out data);
|
||||
components = ColorComponents.RedGreenBlue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,29 +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 "png":
|
||||
{
|
||||
string newPath = Path.Combine(dirPath, name + ".png");
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsPng(newPath);
|
||||
}
|
||||
break;
|
||||
case "bgra":
|
||||
{
|
||||
string newPath = Path.Combine(dirPath, name + ".bgra");
|
||||
WriteAllBytes(newPath, bitmap.Bits);
|
||||
}
|
||||
File.WriteAllBytes(path + ".bgra", bitmap.Bits);
|
||||
break;
|
||||
case "bmp":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsBmp(path + ".bmp");
|
||||
break;
|
||||
case "exr":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsExr(path + ".exr");
|
||||
break;
|
||||
case "hdr":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsHdr(path + ".hdr");
|
||||
break;
|
||||
case "jpg":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsJpg(path + ".jpg");
|
||||
break;
|
||||
case "png":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsPng(path + ".png");
|
||||
break;
|
||||
case "tga":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsTga(path + ".tga");
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(outputType);
|
||||
@@ -94,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)
|
||||
@@ -106,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:
|
||||
@@ -132,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:
|
||||
@@ -168,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:
|
||||
@@ -197,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:
|
||||
@@ -230,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);
|
||||
PvrtcDecoder.DecompressPVRTC<ColorBGRA<byte>, byte>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.ConsoleApp;
|
||||
static partial class Program
|
||||
@@ -9,312 +11,500 @@ static partial class Program
|
||||
private static void DecodeRgb(ReadOnlySpan<byte> input, int width, int height, string modeString, Span<byte> output)
|
||||
{
|
||||
Console.WriteLine("Arg at index 5 : mode");
|
||||
Console.WriteLine(" 0 - R8SByte");
|
||||
Console.WriteLine(" 1 - RG8SByte");
|
||||
Console.WriteLine(" 2 - RGB8SByte");
|
||||
Console.WriteLine(" 3 - RGBA8SByte");
|
||||
Console.WriteLine(" 4 - A8SByte");
|
||||
Console.WriteLine(" 5 - R8Byte");
|
||||
Console.WriteLine(" 6 - RG8Byte");
|
||||
Console.WriteLine(" 7 - RGB8Byte");
|
||||
Console.WriteLine(" 8 - RGBA8Byte");
|
||||
Console.WriteLine(" 9 - A8Byte");
|
||||
Console.WriteLine(" 10 - R16Int16");
|
||||
Console.WriteLine(" 11 - RG16Int16");
|
||||
Console.WriteLine(" 12 - RGB16Int16");
|
||||
Console.WriteLine(" 13 - RGBA16Int16");
|
||||
Console.WriteLine(" 14 - A16Int16");
|
||||
Console.WriteLine(" 15 - R16UInt16");
|
||||
Console.WriteLine(" 16 - RG16UInt16");
|
||||
Console.WriteLine(" 17 - RGB16UInt16");
|
||||
Console.WriteLine(" 18 - RGBA16UInt16");
|
||||
Console.WriteLine(" 19 - A16UInt16");
|
||||
Console.WriteLine(" 20 - R32Int32");
|
||||
Console.WriteLine(" 21 - RG32Int32");
|
||||
Console.WriteLine(" 22 - RGB32Int32");
|
||||
Console.WriteLine(" 23 - RGBA32Int32");
|
||||
Console.WriteLine(" 24 - A32Int32");
|
||||
Console.WriteLine(" 25 - R32UInt32");
|
||||
Console.WriteLine(" 26 - RG32UInt32");
|
||||
Console.WriteLine(" 27 - RGB32UInt32");
|
||||
Console.WriteLine(" 28 - RGBA32UInt32");
|
||||
Console.WriteLine(" 29 - A32UInt32");
|
||||
Console.WriteLine(" 30 - R64Int64");
|
||||
Console.WriteLine(" 31 - RG64Int64");
|
||||
Console.WriteLine(" 32 - RGB64Int64");
|
||||
Console.WriteLine(" 33 - RGBA64Int64");
|
||||
Console.WriteLine(" 34 - A64Int64");
|
||||
Console.WriteLine(" 35 - R64UInt64");
|
||||
Console.WriteLine(" 36 - RG64UInt64");
|
||||
Console.WriteLine(" 37 - RGB64UInt64");
|
||||
Console.WriteLine(" 38 - RGBA64UInt64");
|
||||
Console.WriteLine(" 39 - A64UInt64");
|
||||
Console.WriteLine(" 40 - R128Int128");
|
||||
Console.WriteLine(" 41 - RG128Int128");
|
||||
Console.WriteLine(" 42 - RGB128Int128");
|
||||
Console.WriteLine(" 43 - RGBA128Int128");
|
||||
Console.WriteLine(" 44 - A128Int128");
|
||||
Console.WriteLine(" 45 - R128UInt128");
|
||||
Console.WriteLine(" 46 - RG128UInt128");
|
||||
Console.WriteLine(" 47 - RGB128UInt128");
|
||||
Console.WriteLine(" 48 - RGBA128UInt128");
|
||||
Console.WriteLine(" 49 - A128UInt128");
|
||||
Console.WriteLine(" 50 - R16Half");
|
||||
Console.WriteLine(" 51 - RG16Half");
|
||||
Console.WriteLine(" 52 - RGB16Half");
|
||||
Console.WriteLine(" 53 - RGBA16Half");
|
||||
Console.WriteLine(" 54 - A16Half");
|
||||
Console.WriteLine(" 55 - R32Single");
|
||||
Console.WriteLine(" 56 - RG32Single");
|
||||
Console.WriteLine(" 57 - RGB32Single");
|
||||
Console.WriteLine(" 58 - RGBA32Single");
|
||||
Console.WriteLine(" 59 - A32Single");
|
||||
Console.WriteLine(" 60 - R64Double");
|
||||
Console.WriteLine(" 61 - RG64Double");
|
||||
Console.WriteLine(" 62 - RGB64Double");
|
||||
Console.WriteLine(" 63 - RGBA64Double");
|
||||
Console.WriteLine(" 64 - A64Double");
|
||||
Console.WriteLine(" 65 - R128Decimal");
|
||||
Console.WriteLine(" 66 - RG128Decimal");
|
||||
Console.WriteLine(" 67 - RGB128Decimal");
|
||||
Console.WriteLine(" 68 - RGBA128Decimal");
|
||||
Console.WriteLine(" 69 - A128Decimal");
|
||||
Console.WriteLine(" 70 - ARGB16");
|
||||
Console.WriteLine(" 71 - ARGB32");
|
||||
Console.WriteLine(" 72 - BGRA32");
|
||||
Console.WriteLine(" 73 - RGB16");
|
||||
Console.WriteLine(" 74 - RGB9e5");
|
||||
Console.WriteLine(" 75 - RGBA16");
|
||||
Console.WriteLine(" 0 - R_SByte");
|
||||
Console.WriteLine(" 1 - RG_SByte");
|
||||
Console.WriteLine(" 2 - RGB_SByte");
|
||||
Console.WriteLine(" 3 - RGBA_SByte");
|
||||
Console.WriteLine(" 4 - A_SByte");
|
||||
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<long>, long, 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<long>, long, 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<long>, long, 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<long>, long, 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<long>, long, 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<ulong>, ulong, 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<ulong>, ulong, 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<ulong>, ulong, 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<ulong>, ulong, 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<ulong>, ulong, 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<Int128>, Int128, 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<Int128>, Int128, 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<Int128>, Int128, 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<Int128>, Int128, 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<Int128>, Int128, 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<UInt128>, UInt128, 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<UInt128>, UInt128, 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<UInt128>, UInt128, 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<UInt128>, UInt128, 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<UInt128>, UInt128, 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<Half>, Half, 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<Half>, Half, 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<Half>, Half, 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<Half>, Half, 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<Half>, Half, 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<float>, float, 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<float>, float, 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<float>, float, 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<float>, float, 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<float>, float, 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<double>, double, 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<double>, double, 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<double>, double, 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<double>, double, 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<double>, double, 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<decimal>, decimal, 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<decimal>, decimal, 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<decimal>, decimal, 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<decimal>, decimal, 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<decimal>, decimal, 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<ColorARGB16, byte, 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<ColorARGB32, byte, 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<ColorBGRA32, byte, 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<ColorRGB16, byte, 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<ColorRGB9e5, double, 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<ColorRGBA16, byte, 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<ColorBGRA<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 77:
|
||||
RgbConverter.Convert<ColorR<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 78:
|
||||
RgbConverter.Convert<ColorRG<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 79:
|
||||
RgbConverter.Convert<ColorRGB<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 80:
|
||||
RgbConverter.Convert<ColorRGBA<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 81:
|
||||
RgbConverter.Convert<ColorA<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 82:
|
||||
RgbConverter.Convert<ColorARGB<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 83:
|
||||
RgbConverter.Convert<ColorBGRA<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 84:
|
||||
RgbConverter.Convert<ColorR<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 85:
|
||||
RgbConverter.Convert<ColorRG<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 86:
|
||||
RgbConverter.Convert<ColorRGB<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 87:
|
||||
RgbConverter.Convert<ColorRGBA<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 88:
|
||||
RgbConverter.Convert<ColorA<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 89:
|
||||
RgbConverter.Convert<ColorARGB<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 90:
|
||||
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());
|
||||
|
||||
@@ -1,9 +1,2 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
-6
@@ -1,9 +1,3 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.SourceGeneration.Common;
|
||||
|
||||
@@ -13,12 +14,15 @@ public static class CSharpPrimitives
|
||||
{ typeof(ushort), new Data<ushort>() },
|
||||
{ typeof(int), new Data<int>() },
|
||||
{ typeof(uint), new Data<uint>() },
|
||||
{ typeof(nint), new Data<nint>() },
|
||||
{ typeof(nuint), new Data<nuint>() },
|
||||
{ typeof(long), new Data<long>() },
|
||||
{ typeof(ulong), new Data<ulong>() },
|
||||
{ typeof(Int128), new Data<Int128>() },
|
||||
{ typeof(UInt128), new Data<UInt128>() },
|
||||
{ typeof(Half), new Data<Half>() },
|
||||
{ typeof(float), new Data<float>() },
|
||||
{ typeof(NFloat), new Data<NFloat>() },
|
||||
{ typeof(double), new Data<double>() },
|
||||
{ typeof(decimal), new Data<decimal>() },
|
||||
};
|
||||
@@ -31,16 +35,51 @@ public static class CSharpPrimitives
|
||||
|
||||
public static Data FirstData => List[0];
|
||||
|
||||
public static bool HasPointerSize(Type type, [NotNullWhen(true)] out Type? bit32Type, [NotNullWhen(true)] out Type? bit64Type)
|
||||
{
|
||||
if (type == typeof(nint))
|
||||
{
|
||||
bit32Type = typeof(int);
|
||||
bit64Type = typeof(long);
|
||||
return true;
|
||||
}
|
||||
else if (type == typeof(nuint))
|
||||
{
|
||||
bit32Type = typeof(uint);
|
||||
bit64Type = typeof(ulong);
|
||||
return true;
|
||||
}
|
||||
else if (type == typeof(NFloat))
|
||||
{
|
||||
bit32Type = typeof(float);
|
||||
bit64Type = typeof(double);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bit32Type = null;
|
||||
bit64Type = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsFloatingPoint(Type type)
|
||||
{
|
||||
return type == typeof(Half) || type == typeof(float) || type == typeof(double) || type == typeof(decimal);
|
||||
return type == typeof(Half) || type == typeof(float) || type == typeof(NFloat) || type == typeof(double) || type == typeof(decimal);
|
||||
}
|
||||
|
||||
public static bool IsInteger(Type type)
|
||||
{
|
||||
return IsUnsignedInteger(type) || IsSignedInteger(type);
|
||||
}
|
||||
|
||||
public static bool IsUnsignedInteger(Type type)
|
||||
{
|
||||
return type == typeof(byte) || type == typeof(ushort) || type == typeof(uint) || type == typeof(ulong) || type == typeof(UInt128);
|
||||
return type == typeof(byte) || type == typeof(ushort) || type == typeof(uint) || type == typeof(nuint) || type == typeof(ulong) || type == typeof(UInt128);
|
||||
}
|
||||
|
||||
public static bool IsSignedInteger(Type type) => IsSignedInteger(type, out _);
|
||||
|
||||
public static bool IsSignedInteger(Type type, [NotNullWhen(true)] out Type? unsignedType)
|
||||
{
|
||||
if (type == typeof(sbyte))
|
||||
@@ -55,6 +94,10 @@ public static class CSharpPrimitives
|
||||
{
|
||||
unsignedType = typeof(uint);
|
||||
}
|
||||
else if (type == typeof(nint))
|
||||
{
|
||||
unsignedType = typeof(nuint);
|
||||
}
|
||||
else if (type == typeof(long))
|
||||
{
|
||||
unsignedType = typeof(ulong);
|
||||
@@ -113,6 +156,14 @@ public static class CSharpPrimitives
|
||||
{
|
||||
return "uint";
|
||||
}
|
||||
else if (type == typeof(nint))
|
||||
{
|
||||
return "nint";
|
||||
}
|
||||
else if (type == typeof(nuint))
|
||||
{
|
||||
return "nuint";
|
||||
}
|
||||
else if (type == typeof(long))
|
||||
{
|
||||
return "long";
|
||||
@@ -151,12 +202,42 @@ 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; }
|
||||
public string TypeName => Type.Name;
|
||||
public string LangName => GetLangName(Type);
|
||||
public bool CanBeConstant => CanBeConstant(Type);
|
||||
public bool HasPointerSize([NotNullWhen(true)] out Data? bit32Data, [NotNullWhen(true)] out Data? bit64Data)
|
||||
{
|
||||
if (CSharpPrimitives.HasPointerSize(Type, out Type? bit32Type, out Type? bit64Type))
|
||||
{
|
||||
bit32Data = Dictionary[bit32Type];
|
||||
bit64Data = Dictionary[bit64Type];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bit32Data = null;
|
||||
bit64Data = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public bool IsFloatingPoint => IsFloatingPoint(Type);
|
||||
public bool IsUnsignedInteger => IsUnsignedInteger(Type);
|
||||
public bool IsSignedInteger([NotNullWhen(true)] out Data? unsignedData)
|
||||
@@ -182,6 +263,19 @@ public static class CSharpPrimitives
|
||||
{
|
||||
public override Type Type => typeof(T);
|
||||
|
||||
public override int Size => Unsafe.SizeOf<T>();
|
||||
public override int Size
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CSharpPrimitives.HasPointerSize(Type, out _, out _))
|
||||
{
|
||||
return 6;//arbitrary value between 4 and 8
|
||||
}
|
||||
else
|
||||
{
|
||||
return Unsafe.SizeOf<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -2,9 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -14,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>
|
||||
@@ -21,7 +21,7 @@ namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
|
||||
public string ChannelTypeName => CSharpPrimitives.Dictionary[ChannelType].LangName;
|
||||
|
||||
public static GenerationData Create<T>(int colorSize) where T : IColorBase
|
||||
public static GenerationData Create<T>(int colorSize) where T : IColor
|
||||
{
|
||||
return new GenerationData()
|
||||
{
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
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;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
{
|
||||
@@ -11,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),
|
||||
@@ -34,7 +34,9 @@ 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();
|
||||
|
||||
writer.WriteFileScopedNamespace("AssetRipper.TextureDecoder.ConsoleApp");
|
||||
@@ -60,7 +62,7 @@ namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
writer.WriteLine($"case {i}:");
|
||||
using (new Indented(writer))
|
||||
{
|
||||
writer.WriteLine($"RgbConverter.Convert<{colorName}, {channelName}, ColorBGRA32, byte>(input, width, height, output);");
|
||||
writer.WriteLine($"RgbConverter.Convert<{colorName}, {channelName}, Color<byte, B, G, R, A>, byte>(input, width, height, output);");
|
||||
writer.WriteLine("break;");
|
||||
}
|
||||
}
|
||||
@@ -79,10 +81,9 @@ namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
{
|
||||
foreach (CSharpPrimitives.Data primitiveData in CSharpPrimitives.List)
|
||||
{
|
||||
int primitiveBitSize = primitiveData.Size * 8;
|
||||
foreach (string genericColorName in TestClassGenerator.GetGenericColorNames())
|
||||
{
|
||||
string prettyName = $"{genericColorName}{primitiveBitSize}{primitiveData.TypeName}";
|
||||
string prettyName = $"{genericColorName}_{primitiveData.TypeName}";
|
||||
yield return (prettyName, $"{genericColorName}<{primitiveData.LangName}>", primitiveData.LangName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ internal static class TestClassGenerator
|
||||
private const string GenericOutputNamespace = OutputNamespace + ".Generic";
|
||||
private const string GenericOutputFolder = OutputFolder + "Generic/";
|
||||
|
||||
private static readonly char[] channels = new char[] { 'R', 'G', 'B', 'A' };
|
||||
private static readonly char[] channels = ['R', 'G', 'B', 'A'];
|
||||
|
||||
internal static void GenerateTestClasses()
|
||||
{
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ internal static class TestClassGenerator
|
||||
writer.WriteLine($"[TestFixture(TypeArgs = new Type[] {{ typeof({colorName}<{channeName}>), typeof({channeName}) }})]");
|
||||
}
|
||||
}
|
||||
writer.WriteLine("partial class GenericColorTests<TColor, TChannel>");
|
||||
writer.WriteLine("partial class GenericColorTests<TColor, TChannelValue>");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
}
|
||||
@@ -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("}");
|
||||
@@ -354,4 +356,4 @@ internal static class TestClassGenerator
|
||||
textWriter.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.8.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.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="8.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using AssetRipper.TextureDecoder.Astc;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests;
|
||||
|
||||
@@ -67,11 +68,17 @@ 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));
|
||||
}
|
||||
ByteArrayDeviation.AssertMinimalDeviation(decompressedData, AndroidTextures.Logo_00.Data, maxMeanDeviation, maxStandardDeviation);
|
||||
}
|
||||
|
||||
[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<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")));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using AssetRipper.TextureDecoder.Bc;
|
||||
using AssetRipper.TextureDecoder.Dxt;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
@@ -19,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);
|
||||
}
|
||||
@@ -34,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);
|
||||
}
|
||||
@@ -49,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);
|
||||
}
|
||||
@@ -64,61 +66,125 @@ 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));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC6HFastTest()
|
||||
[TestCase("test.bc6h_fast")]
|
||||
[TestCase("test.bc6h_normal")]
|
||||
[TestCase("test.bc6h_best")]
|
||||
public void Decompress_BC6H(string fileName)
|
||||
{
|
||||
AssertCorrectBC6HDecompression(TestFileFolders.BcTestFiles + "test.bc6h_fast", 512, 512, false);
|
||||
AssertCorrectBC6HDecompression(TestFileFolders.BcTestFiles + fileName, 512, 512, false);
|
||||
}
|
||||
|
||||
[TestCase("test.bc7_fast")]
|
||||
[TestCase("test.bc7_normal")]
|
||||
[TestCase("test.bc7_best")]
|
||||
public void Decompress_BC7(string fileName)
|
||||
{
|
||||
AssertCorrectBC7Decompression(TestFileFolders.BcTestFiles + fileName, 512, 512);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC6HNormalTest()
|
||||
public void PartialBlock_BC1([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
AssertCorrectBC6HDecompression(TestFileFolders.BcTestFiles + "test.bc6h_normal", 512, 512, false);
|
||||
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 DecompressBC6HBestTest()
|
||||
public void PartialBlock_BC2([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
AssertCorrectBC6HDecompression(TestFileFolders.BcTestFiles + "test.bc6h_best", 512, 512, false);
|
||||
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 DecompressBC7FastTest()
|
||||
public void PartialBlock_BC3([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
AssertCorrectBC7Decompression(TestFileFolders.BcTestFiles + "test.bc7_fast", 512, 512);
|
||||
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 DecompressBC7NormalTest()
|
||||
public void PartialBlock_BC4([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
AssertCorrectBC7Decompression(TestFileFolders.BcTestFiles + "test.bc7_normal", 512, 512);
|
||||
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 DecompressBC7BestTest()
|
||||
public void PartialBlock_BC5([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
AssertCorrectBC7Decompression(TestFileFolders.BcTestFiles + "test.bc7_best", 512, 512);
|
||||
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<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>>()));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -126,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));
|
||||
|
||||
@@ -1,109 +1,102 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
using AssetRipper.TextureDecoder.Etc;
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests;
|
||||
|
||||
public sealed class EtcTests
|
||||
{
|
||||
public sealed class EtcTests
|
||||
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);
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2Test() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc2", ETC2Delegate);
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2A1Test() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc2a1", ETC2A1Delegate);
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2A8Test() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc2a8", ETC2A8Delegate);
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRSignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_r", EACRSignedDelegate);
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRUnsignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_r", EACRUnsignedDelegate);
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRGSignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_rg", EACRGSignedDelegate);
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRGUnsignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_rg", EACRGUnsignedDelegate);
|
||||
|
||||
[Test]
|
||||
public void CorrectnessETCTest() => AssertCorrectDecompression<AndroidTextures.Logo_12, ColorRGB<byte>>(ETCDelegate, .05, 2.1);
|
||||
|
||||
[Test]
|
||||
public void CorrectnessETC2Test() => AssertCorrectDecompression<AndroidTextures.Logo_11, ColorRGB<byte>>(ETC2Delegate, .15, 1.6);
|
||||
|
||||
[Test]
|
||||
public void CorrectnessETC2A1Test() => AssertCorrectDecompression<AndroidTextures.Logo_08>(ETC2A1Delegate, .15, 1.7);
|
||||
|
||||
[Test]
|
||||
public void CorrectnessETC2A8Test() => AssertCorrectDecompression<AndroidTextures.Logo_07>(ETC2A8Delegate, .15, 1.6);
|
||||
|
||||
[Test]
|
||||
public void CorrectnessEACRUnsignedTest() => AssertCorrectDecompression<AndroidTextures.Logo_16, ColorR<byte>>(EACRUnsignedDelegate, .05, .5);
|
||||
|
||||
[Test]
|
||||
public void CorrectnessEACRGUnsignedTest() => AssertCorrectDecompression<AndroidTextures.Logo_15, ColorRG<byte>>(EACRGUnsignedDelegate, .1, .6);
|
||||
|
||||
private delegate int DecodingDelegate(ReadOnlySpan<byte> data, int width, int height, out byte[] decompressedData);
|
||||
|
||||
private static void AssertCorrectByteCountReadFor256SquareWithMips(string path, DecodingDelegate decoder)
|
||||
{
|
||||
[Test]
|
||||
public void DecompressETCTest()
|
||||
byte[] data = File.ReadAllBytes(path);
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.etc");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressETC(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
int bytesRead = decoder.Invoke(new ReadOnlySpan<byte>(data, totalBytesRead, data.Length - totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
private static void AssertCorrectDecompression<T>(DecodingDelegate decoder, double maxMeanDeviation, double maxStandardDeviation) where T : ITexture
|
||||
{
|
||||
AssertCorrectDecompression<T, ColorRGBA<byte>>(decoder, maxMeanDeviation, maxStandardDeviation);
|
||||
}
|
||||
|
||||
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 ReadOnlySpan<byte>(data), TTexture.Width, TTexture.Height, out byte[] decompressedData);
|
||||
if (!TTexture.Mips)
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2Test()
|
||||
byte[] comparisonData = AndroidTextures.Logo_00.Data;
|
||||
|
||||
//Remove unused color channels
|
||||
if (typeof(TColor) != typeof(ColorBGRA<byte>) && typeof(TColor) != typeof(ColorRGBA<byte>))
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.etc2");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
Span<ColorBGRA<byte>> pixels = MemoryMarshal.Cast<byte, ColorBGRA<byte>>((Span<byte>)comparisonData);
|
||||
for (int i = 0; i < pixels.Length; i++)
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressETC2(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
pixels[i] = pixels[i].Convert<ColorBGRA<byte>, byte, TColor, byte>().Convert<TColor, byte, ColorBGRA<byte>, byte>();
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2A1Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.etc2a1");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressETC2A1(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2A8Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.etc2a8");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressETC2A8(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRSignedTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.eac_r");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressEACRSigned(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRUnsignedTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.eac_r");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressEACRUnsigned(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRGSignedTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.eac_rg");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressEACRGSigned(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRGUnsignedTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.eac_rg");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressEACRGUnsigned(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
ByteArrayDeviation.AssertMinimalDeviation(decompressedData, comparisonData, maxMeanDeviation, maxStandardDeviation);
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
{
|
||||
@@ -194,6 +182,18 @@ public partial class ColorARGB16Tests
|
||||
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<uint>, uint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<nint>, nint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nuint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<nuint>, nuint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_long()
|
||||
{
|
||||
|
||||
@@ -1,220 +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_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,220 +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_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()
|
||||
{
|
||||
@@ -230,6 +218,30 @@ public partial class ColorRGB16Tests
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<uint>, uint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<nint>, nint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGB_nint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<nint>, nint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nuint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<nuint>, nuint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGB_nuint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<nuint>, nuint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_long()
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
@@ -194,6 +182,18 @@ public partial class ColorRGBA16Tests
|
||||
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<uint>, uint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<nint>, nint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nuint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<nuint>, nuint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_long()
|
||||
{
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
internal static class ColorRandom<TColor, TChannel> where TColor : unmanaged, IColor<TChannel> where TChannel : unmanaged
|
||||
internal static class ColorRandom<TColor, TChannelValue> where TColor : unmanaged, IColor<TChannelValue> where TChannelValue : unmanaged
|
||||
{
|
||||
public static TChannel MakeRandomValue()
|
||||
public static TChannelValue MakeRandomValue()
|
||||
{
|
||||
ulong value = TestContext.CurrentContext.Random.NextULong();
|
||||
return NumericConversion.Convert<ulong, TChannel>(value);
|
||||
return NumericConversion.Convert<ulong, TChannelValue>(value);
|
||||
}
|
||||
|
||||
public static TColor MakeRandomColor()
|
||||
|
||||
@@ -3,19 +3,19 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
|
||||
internal partial class GenericColorTests<TColor, TChannel> where TColor : unmanaged, IColor<TChannel> where TChannel : unmanaged
|
||||
internal partial class GenericColorTests<TColor, TChannelValue> where TColor : unmanaged, IColor<TChannelValue> where TChannelValue : unmanaged
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<TColor>(), Is.EqualTo(Color.GetChannelCount<TColor>() * Unsafe.SizeOf<TChannel>()));
|
||||
Assert.That(Unsafe.SizeOf<TColor>(), Is.EqualTo(Color.GetChannelCount<TColor>() * Unsafe.SizeOf<TChannelValue>()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel r = color.R;
|
||||
TChannelValue r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
@@ -24,7 +24,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel g = color.G;
|
||||
TChannelValue g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
@@ -33,7 +33,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel b = color.B;
|
||||
TChannelValue b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
@@ -42,7 +42,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel a = color.A;
|
||||
TChannelValue a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
@@ -51,96 +51,96 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel g = color.G;
|
||||
TChannel b = color.B;
|
||||
TChannel a = color.A;
|
||||
TChannelValue g = color.G;
|
||||
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]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel r = color.R;
|
||||
TChannel b = color.B;
|
||||
TChannel a = color.A;
|
||||
TChannelValue r = color.R;
|
||||
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]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel r = color.R;
|
||||
TChannel g = color.G;
|
||||
TChannel a = color.A;
|
||||
TChannelValue r = color.R;
|
||||
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]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel r = color.R;
|
||||
TChannel g = color.G;
|
||||
TChannel b = color.B;
|
||||
TChannelValue r = color.R;
|
||||
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]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
color.GetChannels(out TChannel r, out TChannel g, out TChannel b, out TChannel a);
|
||||
Assert.Multiple(() =>
|
||||
color.GetChannels(out TChannelValue r, out TChannelValue g, out TChannelValue b, out TChannelValue a);
|
||||
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]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
color.GetChannels(out TChannel r, out TChannel g, out TChannel b, out TChannel a);
|
||||
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 TChannel MakeRandomValue() => ColorRandom<TColor, TChannel>.MakeRandomValue();
|
||||
private static TChannelValue MakeRandomValue() => ColorRandom<TColor, TChannelValue>.MakeRandomValue();
|
||||
|
||||
private static TColor MakeRandomColor() => ColorRandom<TColor, TChannel>.MakeRandomColor();
|
||||
private static TColor MakeRandomColor() => ColorRandom<TColor, TChannelValue>.MakeRandomColor();
|
||||
}
|
||||
@@ -8,71 +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) })]
|
||||
partial class GenericColorTests<TColor, TChannel>
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<decimal>), typeof(decimal) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<decimal>), typeof(decimal) })]
|
||||
partial class GenericColorTests<TColor, TChannelValue>
|
||||
{
|
||||
}
|
||||
@@ -9,6 +9,8 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(Int128) })]
|
||||
@@ -19,6 +21,8 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(Int128) })]
|
||||
@@ -27,6 +31,8 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(Int128) })]
|
||||
@@ -35,22 +41,40 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(long), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(long), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(long), typeof(Int128) })]
|
||||
@@ -65,9 +89,13 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(UInt128), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(Half), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(Half), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(Half), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(Half), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(float), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(float), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(float), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(NFloat), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(NFloat), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(double), typeof(double) })]
|
||||
partial class LosslessColorTests<T1, T2>
|
||||
{
|
||||
|
||||
+25
-25
@@ -3,32 +3,32 @@ using System.Numerics;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(long), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(Int128), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = [typeof(sbyte), typeof(byte)])]
|
||||
[TestFixture(TypeArgs = [typeof(short), typeof(ushort)])]
|
||||
[TestFixture(TypeArgs = [typeof(int), typeof(uint)])]
|
||||
[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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,15 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
internal static class LosslessConversion
|
||||
{
|
||||
internal static void Assert<TColor1, TChannel1, TColor2, TChannel2>()
|
||||
where TChannel1 : unmanaged
|
||||
where TChannel2 : unmanaged
|
||||
where TColor1 : unmanaged, IColor<TChannel1>
|
||||
where TColor2 : unmanaged, IColor<TChannel2>
|
||||
internal static void Assert<TColor1, TChannelValue1, TColor2, TChannelValue2>()
|
||||
where TChannelValue1 : unmanaged
|
||||
where TChannelValue2 : unmanaged
|
||||
where TColor1 : unmanaged, IColor<TChannelValue1>
|
||||
where TColor2 : unmanaged, IColor<TChannelValue2>
|
||||
{
|
||||
TColor1 original = ColorRandom<TColor1, TChannel1>.MakeRandomColor();
|
||||
TColor2 converted = original.Convert<TColor1, TChannel1, TColor2, TChannel2>();
|
||||
TColor1 convertedBack = converted.Convert<TColor2, TChannel2, TColor1, TChannel1>();
|
||||
TColor1 original = ColorRandom<TColor1, TChannelValue1>.MakeRandomColor();
|
||||
TColor2 converted = original.Convert<TColor1, TChannelValue1, TColor2, TChannelValue2>();
|
||||
TColor1 convertedBack = converted.Convert<TColor2, TChannelValue2, TColor1, TChannelValue1>();
|
||||
NUnit.Framework.Assert.That(convertedBack, Is.EqualTo(original));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,18 +1,43 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
using AssetRipper.TextureDecoder.Pvrtc;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests;
|
||||
|
||||
public sealed class PvrtcTests
|
||||
{
|
||||
public sealed class PvrtcTests
|
||||
[Test]
|
||||
public void DecompressPVRTCTest()
|
||||
{
|
||||
[Test]
|
||||
public void DecompressPVRTCTest()
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.PvrtcTestFiles + "test.pvrtc4");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.PvrtcTestFiles + "test.pvrtc4");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Pvrtc.PvrtcDecoder.DecompressPVRTC(data, size, size, false, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
int bytesRead = PvrtcDecoder.DecompressPVRTC<ColorBGRA<byte>, byte>(data, size, size, false, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
private const double MaxMeanDeviation4 = 0.05;
|
||||
private const double MaxStandardDeviation4 = 1.4;
|
||||
|
||||
private const double MaxMeanDeviation2 = 0.10;
|
||||
private const double MaxStandardDeviation2 = 2.6;
|
||||
|
||||
[Test]
|
||||
public void Decompress_4() => AssertCorrectDecompression<AndroidTextures.Logo_13>(false, MaxMeanDeviation4, MaxStandardDeviation4);
|
||||
|
||||
[Test]
|
||||
public void Decompress_2() => AssertCorrectDecompression<AndroidTextures.Logo_14>(true, MaxMeanDeviation2, MaxStandardDeviation2);
|
||||
|
||||
private static void AssertCorrectDecompression<T>(bool do2bitMode, double maxMeanDeviation, double maxStandardDeviation) where T : ITexture
|
||||
{
|
||||
ReadOnlySpan<byte> data = T.Data;
|
||||
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));
|
||||
}
|
||||
ByteArrayDeviation.AssertMinimalDeviation(decompressedData, AndroidTextures.Logo_00.Data, maxMeanDeviation, maxStandardDeviation);
|
||||
}
|
||||
}
|
||||
+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 _);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
global using AssetRipper.TextureDecoder.Paths;
|
||||
global using NUnit.Framework;
|
||||
global using System.Runtime.InteropServices;
|
||||
@@ -1,5 +1,6 @@
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using AssetRipper.TextureDecoder.Yuy2;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
@@ -12,10 +13,12 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void DecompressYUY2Test()
|
||||
{
|
||||
byte[] input = File.ReadAllBytes(TestFileFolders.Yuy2TestFiles + "test.yuy2");
|
||||
int bytesRead = Yuy2Decoder.DecompressYUY2(input, TestImageWidth, TestImageHeight, out byte[] originalOutput);
|
||||
Assert.That(bytesRead, Is.EqualTo(input.Length));
|
||||
Yuy2Decoder.DecompressYUY2<ColorBGRA32, byte>(input, TestImageWidth, TestImageHeight, out byte[] genericOutput);
|
||||
Assert.That(originalOutput, Is.EqualTo(genericOutput));
|
||||
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<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,42 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<RootNamespace>AssetRipper.TextureDecoder</RootNamespace>
|
||||
<Authors>ds5678</Authors>
|
||||
<Company>AssetRipper</Company>
|
||||
<Version>2.0.0</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<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 - 2023 ds5678</Copyright>
|
||||
<Description>Managed library for decoding textures in multiple formats.</Description>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<DocumentationFile>bin\TextureDecoder.xml</DocumentationFile>
|
||||
<NoWarn>1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="PolySharp" Version="1.13.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<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 - 2026 ds5678</Copyright>
|
||||
<Description>Managed library for decoding textures in multiple formats.</Description>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<DocumentationFile>bin\TextureDecoder.xml</DocumentationFile>
|
||||
<NoWarn>1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.103">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,30 +1,35 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc
|
||||
namespace AssetRipper.TextureDecoder.Astc;
|
||||
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
public static partial class AstcDecoder
|
||||
private struct BlockData
|
||||
{
|
||||
private unsafe struct BlockData
|
||||
{
|
||||
public int bw;
|
||||
public int bh;
|
||||
public int width;
|
||||
public int height;
|
||||
public int part_num;
|
||||
public int dual_plane;
|
||||
public int plane_selector;
|
||||
public int weight_range;
|
||||
/// <summary>
|
||||
/// max: 120
|
||||
/// </summary>
|
||||
public int weight_num;
|
||||
public fixed int cem[4];
|
||||
public int cem_range;
|
||||
/// <summary>
|
||||
/// max: 32
|
||||
/// </summary>
|
||||
public int endpoint_value_num;
|
||||
public fixed int endpoints[4 * 8];
|
||||
public fixed int weights[144 * 2];
|
||||
public fixed int partition[144];
|
||||
}
|
||||
public int bw;
|
||||
public int bh;
|
||||
public int width;
|
||||
public int height;
|
||||
public int part_num;
|
||||
public int dual_plane;
|
||||
public int plane_selector;
|
||||
public int weight_range;
|
||||
/// <summary>
|
||||
/// max: 120
|
||||
/// </summary>
|
||||
public int weight_num;
|
||||
public IntBuffer4 cem;
|
||||
public int cem_range;
|
||||
/// <summary>
|
||||
/// max: 32
|
||||
/// </summary>
|
||||
public int endpoint_value_num;
|
||||
/// <summary>
|
||||
/// 4 * 8
|
||||
/// </summary>
|
||||
public EndpointBuffer endpoints;
|
||||
/// <summary>
|
||||
/// 144 * 2
|
||||
/// </summary>
|
||||
public IntBuffer288 weights;
|
||||
public IntBuffer144 partition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc;
|
||||
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
[InlineArray(4)]
|
||||
private struct EndpointBuffer
|
||||
{
|
||||
private Endpoint _element0;
|
||||
|
||||
[InlineArray(8)]
|
||||
public struct Endpoint
|
||||
{
|
||||
private int _element0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc;
|
||||
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
[InlineArray(144)]
|
||||
private struct IntBuffer144
|
||||
{
|
||||
private int _element0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc;
|
||||
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
[InlineArray(288)]
|
||||
private struct IntBuffer288
|
||||
{
|
||||
private int _element0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc;
|
||||
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
[InlineArray(4)]
|
||||
private struct IntBuffer4
|
||||
{
|
||||
private int _element0;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc
|
||||
namespace AssetRipper.TextureDecoder.Astc;
|
||||
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
public static partial class AstcDecoder
|
||||
private struct IntSeqData
|
||||
{
|
||||
private struct IntSeqData
|
||||
{
|
||||
public int bits;
|
||||
public int nonbits;
|
||||
}
|
||||
public int bits;
|
||||
public int nonbits;
|
||||
}
|
||||
}
|
||||
@@ -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 unsafe 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;
|
||||
@@ -78,7 +87,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeBlockParameters(ReadOnlySpan<byte> input, ref BlockData pBlock)
|
||||
private static void DecodeBlockParameters(ReadOnlySpan<byte> input, ref BlockData pBlock)
|
||||
{
|
||||
pBlock.dual_plane = (input[1] & 4) >> 2;
|
||||
pBlock.weight_range = (input[0] >> 4 & 1) | (input[1] << 2 & 8);
|
||||
@@ -231,7 +240,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeEndpoints(ReadOnlySpan<byte> input, ref BlockData pBlock)
|
||||
private static void DecodeEndpoints(ReadOnlySpan<byte> input, ref BlockData pBlock)
|
||||
{
|
||||
Span<IntSeqData> epSeq = stackalloc IntSeqData[32];
|
||||
DecodeIntseq(input, pBlock.part_num == 1 ? 17 : 29, CemTableA[pBlock.cem_range], CemTableB[pBlock.cem_range], pBlock.endpoint_value_num, false, epSeq);
|
||||
@@ -352,61 +361,40 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
|
||||
Span<int> v = ev;
|
||||
for (int cem = 0, cemOff = 0; cem < pBlock.part_num; v = v.Slice((pBlock.cem[cem] / 4 + 1) * 2), cem++, cemOff += 8)
|
||||
for (int cem = 0; cem < pBlock.part_num; v = v.Slice((pBlock.cem[cem] / 4 + 1) * 2), cem++)
|
||||
{
|
||||
switch (pBlock.cem[cem])
|
||||
{
|
||||
case 0:
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), v[0], v[0], v[0], 255, v[1], v[1], v[1], 255);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], v[0], v[0], v[0], 255, v[1], v[1], v[1], 255);
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
int l0 = (v[0] >> 2) | (v[1] & 0xc0);
|
||||
int l1 = Clamp(l0 + (v[1] & 0x3f));
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), l0, l0, l0, 255, l1, l1, l1, 255);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], l0, l0, l0, 255, l1, l1, l1, 255);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[3]);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[3]);
|
||||
break;
|
||||
case 5:
|
||||
BitTransferSigned(ref v[1], ref v[0]);
|
||||
BitTransferSigned(ref v[3], ref v[2]);
|
||||
v[1] += v[0];
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointClamp(new Span<int>(endpoint, 8), v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[2] + v[3]);
|
||||
}
|
||||
SetEndpointClamp(pBlock.endpoints[cem], v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[2] + v[3]);
|
||||
break;
|
||||
case 6:
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, 255, v[0], v[1], v[2], 255);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, 255, v[0], v[1], v[2], 255);
|
||||
break;
|
||||
case 8:
|
||||
if (v[0] + v[2] + v[4] <= v[1] + v[3] + v[5])
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), v[0], v[2], v[4], 255, v[1], v[3], v[5], 255);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], v[0], v[2], v[4], 255, v[1], v[3], v[5], 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointBlue(new Span<int>(endpoint, 8), v[1], v[3], v[5], 255, v[0], v[2], v[4], 255);
|
||||
}
|
||||
SetEndpointBlue(pBlock.endpoints[cem], v[1], v[3], v[5], 255, v[0], v[2], v[4], 255);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -416,40 +404,25 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
BitTransferSigned(ref v[5], ref v[4]);
|
||||
if (v[1] + v[3] + v[5] >= 0)
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointClamp(new Span<int>(endpoint, 8), v[0], v[2], v[4], 255, v[0] + v[1], v[2] + v[3], v[4] + v[5], 255);
|
||||
}
|
||||
SetEndpointClamp(pBlock.endpoints[cem], v[0], v[2], v[4], 255, v[0] + v[1], v[2] + v[3], v[4] + v[5], 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointBlueClamp(new Span<int>(endpoint, 8), v[0] + v[1], v[2] + v[3], v[4] + v[5], 255, v[0], v[2], v[4], 255);
|
||||
}
|
||||
SetEndpointBlueClamp(pBlock.endpoints[cem], v[0] + v[1], v[2] + v[3], v[4] + v[5], 255, v[0], v[2], v[4], 255);
|
||||
}
|
||||
|
||||
break;
|
||||
case 10:
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, v[4], v[0], v[1], v[2], v[5]);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, v[4], v[0], v[1], v[2], v[5]);
|
||||
break;
|
||||
case 12:
|
||||
if (v[0] + v[2] + v[4] <= v[1] + v[3] + v[5])
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), v[0], v[2], v[4], v[6], v[1], v[3], v[5], v[7]);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], v[0], v[2], v[4], v[6], v[1], v[3], v[5], v[7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointBlue(new Span<int>(endpoint, 8), v[1], v[3], v[5], v[7], v[0], v[2], v[4], v[6]);
|
||||
}
|
||||
SetEndpointBlue(pBlock.endpoints[cem], v[1], v[3], v[5], v[7], v[0], v[2], v[4], v[6]);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -460,17 +433,11 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
BitTransferSigned(ref v[7], ref v[6]);
|
||||
if (v[1] + v[3] + v[5] >= 0)
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointClamp(new Span<int>(endpoint, 8), v[0], v[2], v[4], v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7]);
|
||||
}
|
||||
SetEndpointClamp(pBlock.endpoints[cem], v[0], v[2], v[4], v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointBlueClamp(new Span<int>(endpoint, 8), v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7], v[0], v[2], v[4], v[6]);
|
||||
}
|
||||
SetEndpointBlueClamp(pBlock.endpoints[cem], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7], v[0], v[2], v[4], v[6]);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -478,7 +445,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeWeights(ReadOnlySpan<byte> input, ref BlockData block)
|
||||
private static void DecodeWeights(ReadOnlySpan<byte> input, ref BlockData block)
|
||||
{
|
||||
Span<IntSeqData> wSeq = stackalloc IntSeqData[128];
|
||||
DecodeIntseq(input, 128, WeightPrecTableA[block.weight_range], WeightPrecTableB[block.weight_range], block.weight_num, true, wSeq);
|
||||
@@ -634,7 +601,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void SelectPartition(ReadOnlySpan<byte> input, ref BlockData block)
|
||||
private static void SelectPartition(ReadOnlySpan<byte> input, ref BlockData block)
|
||||
{
|
||||
bool small_block = block.bw * block.bh < 31;
|
||||
int seed = (BinaryPrimitives.ReadInt32LittleEndian(input) >> 13 & 0x3ff) | (block.part_num - 1) << 10;
|
||||
@@ -663,11 +630,11 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
|
||||
|
||||
ReadOnlySpan<int> sh = stackalloc int[2]
|
||||
{
|
||||
ReadOnlySpan<int> sh =
|
||||
[
|
||||
(seed & 2) != 0 ? 4 : 5,
|
||||
block.part_num == 3 ? 6 : 5
|
||||
};
|
||||
];
|
||||
|
||||
if ((seed & 1) != 0)
|
||||
{
|
||||
@@ -716,33 +683,33 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void ApplicateColor(BlockData block, Span<uint> output)
|
||||
private static void ApplicateColor(BlockData block, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
if (block.dual_plane != 0)
|
||||
{
|
||||
Span<int> ps = stackalloc int[] { 0, 0, 0, 0 };
|
||||
Span<int> ps = [0, 0, 0, 0];
|
||||
ps[block.plane_selector] = 1;
|
||||
if (block.part_num > 1)
|
||||
{
|
||||
for (int i = 0; i < block.bw * block.bh; i++)
|
||||
{
|
||||
int p = block.partition[i];
|
||||
byte r = SelectColor(block.endpoints[p * 8 + 0], block.endpoints[p * 8 + 4], block.weights[i * 2 + ps[0]]);
|
||||
byte g = SelectColor(block.endpoints[p * 8 + 1], block.endpoints[p * 8 + 5], block.weights[i * 2 + ps[1]]);
|
||||
byte b = SelectColor(block.endpoints[p * 8 + 2], block.endpoints[p * 8 + 6], block.weights[i * 2 + ps[2]]);
|
||||
byte a = SelectColor(block.endpoints[p * 8 + 3], block.endpoints[p * 8 + 7], block.weights[i * 2 + ps[3]]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
byte r = SelectColor(block.endpoints[p][0], block.endpoints[p][4], block.weights[i * 2 + ps[0]]);
|
||||
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] = new(r, g, b, a);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < block.bw * block.bh; i++)
|
||||
{
|
||||
byte r = SelectColor(block.endpoints[0], block.endpoints[4], block.weights[i * 2 + ps[0]]);
|
||||
byte g = SelectColor(block.endpoints[1], block.endpoints[5], block.weights[i * 2 + ps[1]]);
|
||||
byte b = SelectColor(block.endpoints[2], block.endpoints[6], block.weights[i * 2 + ps[2]]);
|
||||
byte a = SelectColor(block.endpoints[3], block.endpoints[7], block.weights[i * 2 + ps[3]]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
byte r = SelectColor(block.endpoints[0][0], block.endpoints[0][4], block.weights[i * 2 + ps[0]]);
|
||||
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] = new(r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -751,22 +718,22 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
for (int i = 0; i < block.bw * block.bh; i++)
|
||||
{
|
||||
int p = block.partition[i];
|
||||
byte r = SelectColor(block.endpoints[p * 8 + 0], block.endpoints[p * 8 + 4], block.weights[i * 2]);
|
||||
byte g = SelectColor(block.endpoints[p * 8 + 1], block.endpoints[p * 8 + 5], block.weights[i * 2]);
|
||||
byte b = SelectColor(block.endpoints[p * 8 + 2], block.endpoints[p * 8 + 6], block.weights[i * 2]);
|
||||
byte a = SelectColor(block.endpoints[p * 8 + 3], block.endpoints[p * 8 + 7], block.weights[i * 2]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
byte r = SelectColor(block.endpoints[p][0], block.endpoints[p][4], block.weights[i * 2]);
|
||||
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] = new(r, g, b, a);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < block.bw * block.bh; i++)
|
||||
{
|
||||
byte r = SelectColor(block.endpoints[0], block.endpoints[4], block.weights[i * 2]);
|
||||
byte g = SelectColor(block.endpoints[1], block.endpoints[5], block.weights[i * 2]);
|
||||
byte b = SelectColor(block.endpoints[2], block.endpoints[6], block.weights[i * 2]);
|
||||
byte a = SelectColor(block.endpoints[3], block.endpoints[7], block.weights[i * 2]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
byte r = SelectColor(block.endpoints[0][0], block.endpoints[0][4], block.weights[i * 2]);
|
||||
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] = new(r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -891,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)
|
||||
{
|
||||
@@ -925,7 +886,19 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static int GetBits(ReadOnlySpan<byte> input, int bit, int len)
|
||||
{
|
||||
return (BinaryPrimitives.ReadInt32LittleEndian(input[(bit / 8)..]) >> (bit % 8)) & ((1 << len) - 1);
|
||||
ReadOnlySpan<byte> slice = input[(bit / 8)..];
|
||||
int bitBuffer;
|
||||
if (slice.Length >= sizeof(int))
|
||||
{
|
||||
bitBuffer = BinaryPrimitives.ReadInt32LittleEndian(slice);
|
||||
}
|
||||
else
|
||||
{
|
||||
Span<byte> temp = stackalloc byte[sizeof(int)];
|
||||
slice.CopyTo(temp);
|
||||
bitBuffer = BinaryPrimitives.ReadInt32LittleEndian(temp);
|
||||
}
|
||||
return (bitBuffer >> (bit % 8)) & ((1 << len) - 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
@@ -1026,11 +999,13 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static byte SelectColor(int v0, int v1, int weight)
|
||||
{
|
||||
return (byte)(((((v0 << 8 | v0) * (64 - weight) + (v1 << 8 | v1) * weight + 32) >> 6) * 255 + 32768) / 65536);
|
||||
const int SignAdjustment = short.MaxValue + 1;
|
||||
const int MaxValue = ushort.MaxValue + 1;
|
||||
return (byte)(((((v0 << 8 | v0) * (64 - weight) + (v1 << 8 | v1) * weight + 32) >> 6) * byte.MaxValue + SignAdjustment) / MaxValue);
|
||||
}
|
||||
|
||||
private static readonly byte[] BitReverseTable = new byte[]
|
||||
{
|
||||
private static ReadOnlySpan<byte> BitReverseTable =>
|
||||
[
|
||||
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
|
||||
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
|
||||
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
|
||||
@@ -1047,32 +1022,32 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
|
||||
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
|
||||
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
|
||||
};
|
||||
];
|
||||
|
||||
private static readonly int[] WeightPrecTableA = new int[] { 0, 0, 0, 3, 0, 5, 3, 0, 0, 0, 5, 3, 0, 5, 3, 0 };
|
||||
private static readonly int[] WeightPrecTableB = new int[] { 0, 0, 1, 0, 2, 0, 1, 3, 0, 0, 1, 2, 4, 2, 3, 5 };
|
||||
private static ReadOnlySpan<int> WeightPrecTableA => [0, 0, 0, 3, 0, 5, 3, 0, 0, 0, 5, 3, 0, 5, 3, 0];
|
||||
private static ReadOnlySpan<int> WeightPrecTableB => [0, 0, 1, 0, 2, 0, 1, 3, 0, 0, 1, 2, 4, 2, 3, 5];
|
||||
|
||||
private static readonly int[] CemTableA = new int[] { 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 0, 0 };
|
||||
private static readonly int[] CemTableB = new int[] { 8, 6, 5, 7, 5, 4, 6, 4, 3, 5, 3, 2, 4, 2, 1, 3, 1, 2, 1 };
|
||||
private static ReadOnlySpan<int> CemTableA => [0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 0, 0];
|
||||
private static ReadOnlySpan<int> CemTableB => [8, 6, 5, 7, 5, 4, 6, 4, 3, 5, 3, 2, 4, 2, 1, 3, 1, 2, 1];
|
||||
|
||||
private static readonly int[] DImt = new int[] { 0, 2, 4, 5, 7 };
|
||||
private static readonly int[] DImq = new int[] { 0, 3, 5 };
|
||||
private static readonly int[] DITritsTable =
|
||||
{
|
||||
private static ReadOnlySpan<int> DImt => [0, 2, 4, 5, 7];
|
||||
private static ReadOnlySpan<int> DImq => [0, 3, 5];
|
||||
private static ReadOnlySpan<int> DITritsTable =>
|
||||
[
|
||||
0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2,
|
||||
0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1,
|
||||
0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
|
||||
};
|
||||
private static readonly int[] DIQuintsTable =
|
||||
{
|
||||
];
|
||||
private static ReadOnlySpan<int> DIQuintsTable =>
|
||||
[
|
||||
0, 1, 2, 3, 4, 0, 4, 4, 0, 1, 2, 3, 4, 1, 4, 4, 0, 1, 2, 3, 4, 2, 4, 4, 0, 1, 2, 3, 4, 3, 4, 4, 0, 1, 2, 3, 4, 0, 4, 0, 0, 1, 2, 3, 4, 1, 4, 1, 0, 1, 2, 3, 4, 2, 4, 2, 0, 1, 2, 3, 4, 3, 4, 3, 0, 1, 2, 3, 4, 0, 2, 3, 0, 1, 2, 3, 4, 1, 2, 3, 0, 1, 2, 3, 4, 2, 2, 3, 0, 1, 2, 3, 4, 3, 2, 3, 0, 1, 2, 3, 4, 0, 0, 1, 0, 1, 2, 3, 4, 1, 0, 1, 0, 1, 2, 3, 4, 2, 0, 1, 0, 1, 2, 3, 4, 3, 0, 1,
|
||||
0, 0, 0, 0, 0, 4, 4, 4, 1, 1, 1, 1, 1, 4, 4, 4, 2, 2, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 4, 0, 4, 1, 1, 1, 1, 1, 4, 1, 4, 2, 2, 2, 2, 2, 4, 2, 4, 3, 3, 3, 3, 3, 4, 3, 4, 0, 0, 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 1, 4, 1, 1, 2, 2, 2, 2, 2, 4, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3, 0, 0, 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 1, 4, 1, 1, 2, 2, 2, 2, 2, 4, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3,
|
||||
0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 3, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4,
|
||||
};
|
||||
];
|
||||
|
||||
private static readonly int[] DETritsTable = new int[] { 0, 204, 93, 44, 22, 11, 5 };
|
||||
private static readonly int[] DEQuintsTable = new int[] { 0, 113, 54, 26, 13, 6 };
|
||||
private static ReadOnlySpan<int> DETritsTable => [0, 204, 93, 44, 22, 11, 5];
|
||||
private static ReadOnlySpan<int> DEQuintsTable => [0, 113, 54, 26, 13, 6];
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,48 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,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);
|
||||
@@ -84,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);
|
||||
@@ -113,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;
|
||||
}
|
||||
@@ -122,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));
|
||||
}
|
||||
|
||||
@@ -160,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,29 +5,62 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc1
|
||||
{
|
||||
internal const int BlockSize = 8;
|
||||
/// <summary>
|
||||
/// 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;
|
||||
}
|
||||
|
||||
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
|
||||
}
|
||||
}
|
||||
@@ -5,29 +5,62 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc2
|
||||
{
|
||||
internal const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// 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;
|
||||
}
|
||||
|
||||
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
|
||||
}
|
||||
}
|
||||
@@ -5,29 +5,62 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc3
|
||||
{
|
||||
internal const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// 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;
|
||||
}
|
||||
|
||||
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
|
||||
}
|
||||
}
|
||||
@@ -6,33 +6,62 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc4
|
||||
{
|
||||
internal const int BlockSize = 8;
|
||||
/// <summary>
|
||||
/// 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;
|
||||
}
|
||||
|
||||
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
|
||||
}
|
||||
}
|
||||
@@ -6,33 +6,62 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc5
|
||||
{
|
||||
internal const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// 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;
|
||||
}
|
||||
|
||||
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
|
||||
}
|
||||
}
|
||||
@@ -6,37 +6,62 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc6h
|
||||
{
|
||||
internal const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// 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;
|
||||
}
|
||||
|
||||
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
|
||||
}
|
||||
}
|
||||
@@ -2,248 +2,216 @@
|
||||
|
||||
internal static class Bc6hTables
|
||||
{
|
||||
internal static byte[][] ActualBitsCount { get; } = new byte[4][]
|
||||
{
|
||||
new byte[14] {10, 7, 11, 11, 11, 9, 8, 8, 8, 6, 10, 11, 12, 16},
|
||||
new byte[14] {5, 6, 5, 4, 4, 5, 6, 5, 5, 6, 10, 9, 8, 4},
|
||||
new byte[14] {5, 6, 4, 5, 4, 5, 5, 6, 5, 6, 10, 9, 8, 4},
|
||||
new byte[14] {5, 6, 4, 4, 5, 5, 5, 5, 6, 6, 10, 9, 8, 4}
|
||||
};
|
||||
internal static byte[][] ActualBitsCount { get; } =
|
||||
[
|
||||
[10, 7, 11, 11, 11, 9, 8, 8, 8, 6, 10, 11, 12, 16],
|
||||
[5, 6, 5, 4, 4, 5, 6, 5, 5, 6, 10, 9, 8, 4],
|
||||
[5, 6, 4, 5, 4, 5, 5, 6, 5, 6, 10, 9, 8, 4],
|
||||
[5, 6, 4, 4, 5, 5, 5, 5, 6, 6, 10, 9, 8, 4]
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// There are 32 possible partition sets for a two-region tile.
|
||||
/// Each 4x4 block represents a single shape.
|
||||
/// Here also every fix-up index has MSB bit set.
|
||||
/// </summary>
|
||||
internal static byte[][][] PartitionSets { get; } = new byte[32][][]
|
||||
{
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 1, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 1, 1, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 1, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 1, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {1, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 0},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 1, 129, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {129, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 1, 129, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 129, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {1, 0, 0, 0},
|
||||
new byte[4] {129, 1, 0, 0},
|
||||
new byte[4] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {129, 0, 0, 0},
|
||||
new byte[4] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 1, 1, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 0, 0, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 129, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {1, 0, 0, 0},
|
||||
new byte[4] {129, 0, 0, 0},
|
||||
new byte[4] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 1, 129, 0},
|
||||
new byte[4] {0, 1, 1, 0},
|
||||
new byte[4] {0, 1, 1, 0},
|
||||
new byte[4] {0, 1, 1, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 129, 1},
|
||||
new byte[4] {0, 1, 1, 0},
|
||||
new byte[4] {0, 1, 1, 0},
|
||||
new byte[4] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {129, 1, 1, 0},
|
||||
new byte[4] {1, 0, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 1},
|
||||
new byte[4] {129, 1, 1, 1},
|
||||
new byte[4] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 1, 129, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {1, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 129, 1},
|
||||
new byte[4] {1, 0, 0, 1},
|
||||
new byte[4] {1, 0, 0, 1},
|
||||
new byte[4] {1, 1, 0, 0}
|
||||
}
|
||||
};
|
||||
internal static byte[][][] PartitionSets { get; } =
|
||||
[
|
||||
[
|
||||
[128, 0, 1, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 129]
|
||||
],
|
||||
[
|
||||
[128, 1, 1, 1],
|
||||
[0, 1, 1, 1],
|
||||
[0, 1, 1, 1],
|
||||
[0, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 1, 1],
|
||||
[0, 1, 1, 1],
|
||||
[0, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 1, 1],
|
||||
[0, 1, 1, 1],
|
||||
[1, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 1],
|
||||
[0, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[0, 0, 0, 1],
|
||||
[0, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 1],
|
||||
[0, 1, 1, 1],
|
||||
[1, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[1, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[1, 1, 1, 1],
|
||||
[1, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[1, 0, 0, 0],
|
||||
[1, 1, 1, 0],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 1, 129, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0],
|
||||
[0, 0, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[129, 0, 0, 0],
|
||||
[1, 1, 1, 0]
|
||||
],
|
||||
[
|
||||
[128, 1, 129, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 129, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0],
|
||||
[0, 0, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[1, 0, 0, 0],
|
||||
[129, 1, 0, 0],
|
||||
[1, 1, 1, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[129, 0, 0, 0],
|
||||
[1, 1, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 1, 1, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 0, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 129, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[1, 0, 0, 0],
|
||||
[129, 0, 0, 0],
|
||||
[1, 1, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 1, 129, 0],
|
||||
[0, 1, 1, 0],
|
||||
[0, 1, 1, 0],
|
||||
[0, 1, 1, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 129, 1],
|
||||
[0, 1, 1, 0],
|
||||
[0, 1, 1, 0],
|
||||
[1, 1, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 1],
|
||||
[0, 1, 1, 1],
|
||||
[129, 1, 1, 0],
|
||||
[1, 0, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[1, 1, 1, 1],
|
||||
[129, 1, 1, 1],
|
||||
[0, 0, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 1, 129, 1],
|
||||
[0, 0, 0, 1],
|
||||
[1, 0, 0, 0],
|
||||
[1, 1, 1, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 129, 1],
|
||||
[1, 0, 0, 1],
|
||||
[1, 0, 0, 1],
|
||||
[1, 1, 0, 0]
|
||||
]
|
||||
];
|
||||
|
||||
internal static int[] AWeight3 { get; } = new int[8] { 0, 9, 18, 27, 37, 46, 55, 64 };
|
||||
internal static ReadOnlySpan<int> AWeight3 => [0, 9, 18, 27, 37, 46, 55, 64];
|
||||
|
||||
internal static int[] AWeight4 { get; } = new int[16] { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
|
||||
internal static ReadOnlySpan<int> AWeight4 => [0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64];
|
||||
}
|
||||
@@ -5,30 +5,60 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc7
|
||||
{
|
||||
internal const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// 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.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;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,47 +1,57 @@
|
||||
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)
|
||||
{
|
||||
BitStream bstream = new()
|
||||
{
|
||||
low = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock),
|
||||
high = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock.Slice(sizeof(ulong)))
|
||||
};
|
||||
Span<int> r = stackalloc int[4]; // wxyz
|
||||
Span<int> g = stackalloc int[4];
|
||||
Span<int> b = stackalloc int[4];
|
||||
Debug.Assert(decompressedBlock.Length == Bc6h.DecodedBlockSize);
|
||||
|
||||
BitStream bstream = new(compressedBlock);
|
||||
Span<uint> r = stackalloc uint[4]; // wxyz
|
||||
Span<uint> g = stackalloc uint[4];
|
||||
Span<uint> b = stackalloc uint[4];
|
||||
|
||||
int decompressedOffset = 0;
|
||||
|
||||
@@ -52,7 +62,7 @@ internal static class BcHelpers
|
||||
int mode;
|
||||
|
||||
//modes >= 11 (10 in my code) are using 0 one, others will read it from the bitstream
|
||||
int partition;
|
||||
uint partition;
|
||||
|
||||
switch (ReadBc6hModeBits(ref bstream))
|
||||
{
|
||||
@@ -452,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;
|
||||
}
|
||||
@@ -477,7 +479,7 @@ internal static class BcHelpers
|
||||
|
||||
// Mode 11 (like Mode 10) does not use delta compression,
|
||||
// and instead stores both color endpoints explicitly.
|
||||
if ((mode != 9 && mode != 10) || isSigned)
|
||||
if ((mode is not 9 and not 10) || isSigned)
|
||||
{
|
||||
for (int i = 1; i < (numPartitions + 1) * 2; ++i)
|
||||
{
|
||||
@@ -487,7 +489,7 @@ internal static class BcHelpers
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != 9 && mode != 10)
|
||||
if (mode is not 9 and not 10)
|
||||
{
|
||||
for (int i = 1; i < (numPartitions + 1) * 2; ++i)
|
||||
{
|
||||
@@ -520,7 +522,7 @@ internal static class BcHelpers
|
||||
}
|
||||
partitionSet &= 0x01;
|
||||
|
||||
int index = bstream.ReadBits(indexBits);
|
||||
int index = bstream.ReadBits(indexBits).ToSigned();
|
||||
|
||||
int ep_i = (partitionSet * 2);
|
||||
|
||||
@@ -539,15 +541,15 @@ internal static class BcHelpers
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(decompressedBlock.Slice(bOffset), bFinal);
|
||||
}
|
||||
|
||||
decompressedOffset += destinationPitch * sizeof(ushort);
|
||||
decompressedOffset += 4 * 3 * sizeof(ushort);
|
||||
}
|
||||
|
||||
static int ReadBc6hModeBits(ref BitStream bstream)
|
||||
static uint ReadBc6hModeBits(ref BitStream bstream)
|
||||
{
|
||||
int twoBits = bstream.ReadBits(2);
|
||||
uint twoBits = bstream.ReadBits(2);
|
||||
if (twoBits > 1)
|
||||
{
|
||||
int threeBits = bstream.ReadBits(3);
|
||||
uint threeBits = bstream.ReadBits(3);
|
||||
return (threeBits << 2) | twoBits;
|
||||
}
|
||||
else
|
||||
@@ -557,68 +559,61 @@ 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)
|
||||
{
|
||||
BitStream bstream = new()
|
||||
{
|
||||
low = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock),
|
||||
high = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock.Slice(sizeof(ulong)))
|
||||
};
|
||||
int[][] endpoints = CreateRectangularArray<int>(6, 4);
|
||||
int[][] indices = CreateRectangularArray<int>(4, 4);
|
||||
Debug.Assert(decompressedBlock.Length == Bc7.DecodedBlockSize);
|
||||
|
||||
int decompressedOffset = 0;
|
||||
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 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;
|
||||
}
|
||||
|
||||
int partition = 0;
|
||||
uint partition = 0;
|
||||
int numPartitions = 1;
|
||||
int rotation = 0;
|
||||
int indexSelectionBit = 0;
|
||||
uint rotation = 0;
|
||||
uint indexSelectionBit = 0;
|
||||
|
||||
if (mode == 0 || mode == 1 || mode == 2 || mode == 3 || mode == 7)
|
||||
switch (mode)
|
||||
{
|
||||
numPartitions = (mode == 0 || mode == 2) ? 3 : 2;
|
||||
partition = bstream.ReadBits((mode == 0) ? 4 : 6);
|
||||
case 0:
|
||||
numPartitions = 3;
|
||||
partition = bstream.ReadBits(4);
|
||||
break;
|
||||
case 1 or 3 or 7:
|
||||
numPartitions = 2;
|
||||
partition = bstream.ReadBits(6);
|
||||
break;
|
||||
case 2:
|
||||
numPartitions = 3;
|
||||
partition = bstream.ReadBits(6);
|
||||
break;
|
||||
case 4:
|
||||
rotation = bstream.ReadBits(2);
|
||||
indexSelectionBit = bstream.ReadBit();
|
||||
break;
|
||||
case 5:
|
||||
rotation = bstream.ReadBits(2);
|
||||
break;
|
||||
}
|
||||
|
||||
int numEndpoints = numPartitions * 2;
|
||||
|
||||
if (mode == 4 || mode == 5)
|
||||
{
|
||||
rotation = bstream.ReadBits(2);
|
||||
|
||||
if (mode == 4)
|
||||
{
|
||||
indexSelectionBit = bstream.ReadBit();
|
||||
}
|
||||
}
|
||||
|
||||
// Extract endpoints
|
||||
// RGB
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
for (int j = 0; j < numEndpoints; ++j)
|
||||
{
|
||||
endpoints[j][i] = bstream.ReadBits(Bc7Tables.ActualBitsCount[0][mode]);
|
||||
endpoints[j, i] = bstream.ReadBits(Bc7Tables.ActualBitsCount[0][mode]);
|
||||
}
|
||||
}
|
||||
// Alpha (if any)
|
||||
@@ -626,7 +621,7 @@ internal static class BcHelpers
|
||||
{
|
||||
for (int j = 0; j < numEndpoints; ++j)
|
||||
{
|
||||
endpoints[j][3] = bstream.ReadBits(Bc7Tables.ActualBitsCount[1][mode]);
|
||||
endpoints[j, 3] = bstream.ReadBits(Bc7Tables.ActualBitsCount[1][mode]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -639,23 +634,23 @@ internal static class BcHelpers
|
||||
// component-wise left-shift
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
endpoints[i][j] <<= 1;
|
||||
endpoints[i, j] <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// if P-bit is shared
|
||||
if (mode == 1)
|
||||
{
|
||||
int i = bstream.ReadBit();
|
||||
int j = bstream.ReadBit();
|
||||
uint i = bstream.ReadBit();
|
||||
uint j = bstream.ReadBit();
|
||||
|
||||
// rgb component-wise insert pbits
|
||||
for (int k = 0; k < 3; ++k)
|
||||
{
|
||||
endpoints[0][k] |= i;
|
||||
endpoints[1][k] |= i;
|
||||
endpoints[2][k] |= j;
|
||||
endpoints[3][k] |= j;
|
||||
endpoints[0, k] |= i;
|
||||
endpoints[1, k] |= i;
|
||||
endpoints[2, k] |= j;
|
||||
endpoints[3, k] |= j;
|
||||
}
|
||||
}
|
||||
else if ((Bc7Tables.bcdec_bc7_sModeHasPBits & (1 << mode)) != 0)
|
||||
@@ -663,10 +658,10 @@ internal static class BcHelpers
|
||||
// unique P-bit per endpoint
|
||||
for (int i = 0; i < numEndpoints; ++i)
|
||||
{
|
||||
int j = bstream.ReadBit();
|
||||
uint j = bstream.ReadBit();
|
||||
for (int k = 0; k < 4; ++k)
|
||||
{
|
||||
endpoints[i][k] |= j;
|
||||
endpoints[i, k] |= j;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -680,18 +675,18 @@ internal static class BcHelpers
|
||||
for (int k = 0; k < 3; ++k)
|
||||
{
|
||||
// left shift endpoint components so that their MSB lies in bit 7
|
||||
endpoints[i][k] = endpoints[i][k] << (8 - j);
|
||||
endpoints[i, k] = endpoints[i, k] << (8 - j);
|
||||
// Replicate each component's MSB into the LSBs revealed by the left-shift operation above
|
||||
endpoints[i][k] = endpoints[i][k] | (endpoints[i][k] >> j);
|
||||
endpoints[i, k] = endpoints[i, k] | (endpoints[i, k] >> j);
|
||||
}
|
||||
|
||||
// get alpha component precision including pbit
|
||||
j = Bc7Tables.ActualBitsCount[1][mode] + ((Bc7Tables.bcdec_bc7_sModeHasPBits >> mode) & 1);
|
||||
|
||||
// left shift endpoint components so that their MSB lies in bit 7
|
||||
endpoints[i][3] = endpoints[i][3] << (8 - j);
|
||||
endpoints[i, 3] = endpoints[i, 3] << (8 - j);
|
||||
// Replicate each component's MSB into the LSBs revealed by the left-shift operation above
|
||||
endpoints[i][3] = endpoints[i][3] | (endpoints[i][3] >> j);
|
||||
endpoints[i, 3] = endpoints[i, 3] | (endpoints[i, 3] >> j);
|
||||
}
|
||||
|
||||
// If this mode does not explicitly define the alpha component
|
||||
@@ -700,15 +695,30 @@ internal static class BcHelpers
|
||||
{
|
||||
for (int j = 0; j < numEndpoints; ++j)
|
||||
{
|
||||
endpoints[j][3] = 0xFF;
|
||||
endpoints[j, 3] = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine weights tables
|
||||
int indexBits = (mode == 0 || mode == 1) ? 3 : ((mode == 6) ? 4 : 2);
|
||||
int indexBits2 = (mode == 4) ? 3 : ((mode == 5) ? 2 : 0);
|
||||
int[] weights = (indexBits == 2) ? Bc7Tables.AWeight2 : ((indexBits == 3) ? Bc7Tables.AWeight3 : Bc7Tables.AWeight4);
|
||||
int[] weights2 = (indexBits2 == 2) ? Bc7Tables.AWeight2 : Bc7Tables.AWeight3;
|
||||
int indexBits = mode switch
|
||||
{
|
||||
0 or 1 => 3,
|
||||
6 => 4,
|
||||
_ => 2
|
||||
};
|
||||
int indexBits2 = mode switch
|
||||
{
|
||||
4 => 3,
|
||||
5 => 2,
|
||||
_ => 0
|
||||
};
|
||||
ReadOnlySpan<int> weights = indexBits switch
|
||||
{
|
||||
2 => Bc7Tables.AWeight2,
|
||||
3 => Bc7Tables.AWeight3,
|
||||
_ => Bc7Tables.AWeight4
|
||||
};
|
||||
ReadOnlySpan<int> weights2 = (indexBits2 == 2) ? Bc7Tables.AWeight2 : Bc7Tables.AWeight3;
|
||||
|
||||
// Quite inconvenient that indices aren't interleaved so we have to make 2 passes here
|
||||
// Pass #1: collecting color indices
|
||||
@@ -726,7 +736,7 @@ internal static class BcHelpers
|
||||
indexBits--;
|
||||
}
|
||||
|
||||
indices[i][j] = bstream.ReadBits(indexBits);
|
||||
indices[i, j] = bstream.ReadBits(indexBits).ToSigned();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -740,23 +750,23 @@ internal static class BcHelpers
|
||||
: Bc7Tables.PartitionSets[numPartitions - 2][partition][i][j];
|
||||
partitionSet &= 0x03;
|
||||
|
||||
int index = indices[i][j];
|
||||
int index = indices[i, j];
|
||||
|
||||
int r;
|
||||
int g;
|
||||
int b;
|
||||
int a;
|
||||
uint r;
|
||||
uint g;
|
||||
uint b;
|
||||
uint a;
|
||||
|
||||
if (indexBits2 == 0)
|
||||
{
|
||||
r = Interpolate(endpoints[partitionSet * 2][0], endpoints[(partitionSet * 2) + 1][0], weights, index);
|
||||
g = Interpolate(endpoints[partitionSet * 2][1], endpoints[(partitionSet * 2) + 1][1], weights, index);
|
||||
b = Interpolate(endpoints[partitionSet * 2][2], endpoints[(partitionSet * 2) + 1][2], weights, index);
|
||||
a = Interpolate(endpoints[partitionSet * 2][3], endpoints[(partitionSet * 2) + 1][3], weights, index);
|
||||
r = Interpolate(endpoints[partitionSet * 2, 0], endpoints[(partitionSet * 2) + 1, 0], weights, index);
|
||||
g = Interpolate(endpoints[partitionSet * 2, 1], endpoints[(partitionSet * 2) + 1, 1], weights, index);
|
||||
b = Interpolate(endpoints[partitionSet * 2, 2], endpoints[(partitionSet * 2) + 1, 2], weights, index);
|
||||
a = Interpolate(endpoints[partitionSet * 2, 3], endpoints[(partitionSet * 2) + 1, 3], weights, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
int index2 = bstream.ReadBits((i | j) != 0 ? indexBits2 : (indexBits2 - 1));
|
||||
int index2 = bstream.ReadBits((i | j) != 0 ? indexBits2 : (indexBits2 - 1)).ToSigned();
|
||||
// The index value for interpolating color comes from the secondary index bits for the texel
|
||||
// if the mode has an index selection bit and its value is one, and from the primary index bits otherwise.
|
||||
// The alpha index comes from the secondary index bits if the block has a secondary index
|
||||
@@ -764,17 +774,17 @@ internal static class BcHelpers
|
||||
// and from the primary index bits otherwise.
|
||||
if (indexSelectionBit == 0)
|
||||
{
|
||||
r = Interpolate(endpoints[partitionSet * 2][0], endpoints[(partitionSet * 2) + 1][0], weights, index);
|
||||
g = Interpolate(endpoints[partitionSet * 2][1], endpoints[(partitionSet * 2) + 1][1], weights, index);
|
||||
b = Interpolate(endpoints[partitionSet * 2][2], endpoints[(partitionSet * 2) + 1][2], weights, index);
|
||||
a = Interpolate(endpoints[partitionSet * 2][3], endpoints[(partitionSet * 2) + 1][3], weights2, index2);
|
||||
r = Interpolate(endpoints[partitionSet * 2, 0], endpoints[(partitionSet * 2) + 1, 0], weights, index);
|
||||
g = Interpolate(endpoints[partitionSet * 2, 1], endpoints[(partitionSet * 2) + 1, 1], weights, index);
|
||||
b = Interpolate(endpoints[partitionSet * 2, 2], endpoints[(partitionSet * 2) + 1, 2], weights, index);
|
||||
a = Interpolate(endpoints[partitionSet * 2, 3], endpoints[(partitionSet * 2) + 1, 3], weights2, index2);
|
||||
}
|
||||
else
|
||||
{
|
||||
r = Interpolate(endpoints[partitionSet * 2][0], endpoints[(partitionSet * 2) + 1][0], weights2, index2);
|
||||
g = Interpolate(endpoints[partitionSet * 2][1], endpoints[(partitionSet * 2) + 1][1], weights2, index2);
|
||||
b = Interpolate(endpoints[partitionSet * 2][2], endpoints[(partitionSet * 2) + 1][2], weights2, index2);
|
||||
a = Interpolate(endpoints[partitionSet * 2][3], endpoints[(partitionSet * 2) + 1][3], weights, index);
|
||||
r = Interpolate(endpoints[partitionSet * 2, 0], endpoints[(partitionSet * 2) + 1, 0], weights2, index2);
|
||||
g = Interpolate(endpoints[partitionSet * 2, 1], endpoints[(partitionSet * 2) + 1, 1], weights2, index2);
|
||||
b = Interpolate(endpoints[partitionSet * 2, 2], endpoints[(partitionSet * 2) + 1, 2], weights2, index2);
|
||||
a = Interpolate(endpoints[partitionSet * 2, 3], endpoints[(partitionSet * 2) + 1, 3], weights, index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -800,23 +810,23 @@ 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)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < 8 && bstream.ReadBit() == 0)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
// Read bits until we find a non-zero bit, up to 8 bits.
|
||||
// The mode is the number of bits read.
|
||||
uint eightBits = bstream.PeakBits(8);
|
||||
int zeros = BitOperations.TrailingZeroCount(eightBits);
|
||||
int bitCount = zeros >= 8 ? 8 : zeros + 1;
|
||||
bstream.Advance(bitCount);
|
||||
return int.Min(zeros, 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -949,6 +959,11 @@ internal static class BcHelpers
|
||||
return (val << (32 - bits)) >> (32 - bits);
|
||||
}
|
||||
|
||||
private static uint ExtendSign(uint val, int bits)
|
||||
{
|
||||
return ExtendSign(val.ToSigned(), bits).ToUnsigned();
|
||||
}
|
||||
|
||||
public static int TransformInverse(int val, int a0, int bits, bool isSigned)
|
||||
{
|
||||
// If the precision of A0 is "p" bits, then the transform algorithm is:
|
||||
@@ -961,6 +976,11 @@ internal static class BcHelpers
|
||||
return val;
|
||||
}
|
||||
|
||||
private static uint TransformInverse(uint val, uint a0, int bits, bool isSigned)
|
||||
{
|
||||
return TransformInverse(val.ToSigned(), a0.ToSigned(), bits, isSigned).ToUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Essentially copy-paste from documentation
|
||||
/// </summary>
|
||||
@@ -1028,11 +1048,21 @@ internal static class BcHelpers
|
||||
return unq;
|
||||
}
|
||||
|
||||
private static uint Unquantize(uint val, int bits, bool isSigned)
|
||||
{
|
||||
return Unquantize(val.ToSigned(), bits, isSigned).ToUnsigned();
|
||||
}
|
||||
|
||||
public static int Interpolate(int a, int b, ReadOnlySpan<int> weights, int index)
|
||||
{
|
||||
return ((a * (64 - weights[index])) + (b * weights[index]) + 32) >> 6;
|
||||
}
|
||||
|
||||
private static uint Interpolate(uint a, uint b, ReadOnlySpan<int> weights, int index)
|
||||
{
|
||||
return Interpolate(a.ToSigned(), b.ToSigned(), weights, index).ToUnsigned();
|
||||
}
|
||||
|
||||
public static ushort FinishUnquantize(int val, bool isSigned)
|
||||
{
|
||||
if (!isSigned)
|
||||
@@ -1041,7 +1071,7 @@ internal static class BcHelpers
|
||||
}
|
||||
else
|
||||
{
|
||||
val = (val < 0) ? -(((-val) * 31) >> 5) : (val * 31) >> 5; // scale the magnitude by 31 / 32
|
||||
val = (val < 0) ? (((-val) * 31) >> 5) : (val * 31) >> 5; // scale the magnitude by 31 / 32
|
||||
int s = 0;
|
||||
if (val < 0)
|
||||
{
|
||||
@@ -1052,19 +1082,35 @@ internal static class BcHelpers
|
||||
}
|
||||
}
|
||||
|
||||
public static void SwapValues(ref int a, ref int b)
|
||||
private static ushort FinishUnquantize(uint val, bool isSigned)
|
||||
{
|
||||
return FinishUnquantize(val.ToSigned(), isSigned);
|
||||
}
|
||||
|
||||
public static void SwapValues(ref uint a, ref uint b)
|
||||
{
|
||||
(a, b) = (b, a);
|
||||
}
|
||||
|
||||
public static T[][] CreateRectangularArray<T>(int size1, int size2)
|
||||
/// <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>
|
||||
{
|
||||
T[][] newArray = new T[size1][];
|
||||
for (int array1 = 0; array1 < size1; array1++)
|
||||
int hEnd = int.Min(h + blockHeight, height);
|
||||
int wEnd = int.Min(w + blockWidth, width);
|
||||
for (int i = h; i < hEnd; i++)
|
||||
{
|
||||
newArray[array1] = new T[size2];
|
||||
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>();
|
||||
}
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,59 @@
|
||||
namespace AssetRipper.TextureDecoder.Bc;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
internal struct BitStream
|
||||
namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
internal ref struct BitStream
|
||||
{
|
||||
public ulong low;
|
||||
public ulong high;
|
||||
|
||||
public int ReadBits(int numBits)
|
||||
private UInt128 value;
|
||||
|
||||
private readonly uint BottomBits
|
||||
{
|
||||
uint mask = (uint)((1 << numBits) - 1);
|
||||
// Read the low N bits
|
||||
uint bits = (uint)(this.low & mask);
|
||||
|
||||
this.low >>= numBits;
|
||||
// Put the low N bits of "high" into the high 64-N bits of "low".
|
||||
this.low |= (this.high & mask) << ((sizeof(ulong) * 8) - numBits);
|
||||
this.high >>= numBits;
|
||||
|
||||
return (int)bits;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
get => unchecked((uint)this.value);
|
||||
}
|
||||
|
||||
public int ReadBit()
|
||||
public BitStream(UInt128 value)
|
||||
{
|
||||
return this.ReadBits(1);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public BitStream(ReadOnlySpan<byte> span) : this(BinaryPrimitives.ReadUInt128LittleEndian(span))
|
||||
{
|
||||
}
|
||||
|
||||
public BitStream(ulong low, ulong high)
|
||||
{
|
||||
value = new UInt128(high, low);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public readonly uint PeakBits(int numBits)
|
||||
{
|
||||
uint mask = (1u << numBits) - 1u;
|
||||
uint bits = BottomBits & mask;
|
||||
return bits;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void Advance(int numBits)
|
||||
{
|
||||
this.value >>= numBits;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public uint ReadBits(int numBits)
|
||||
{
|
||||
uint bits = PeakBits(numBits);
|
||||
Advance(numBits);
|
||||
return bits;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public uint ReadBit()
|
||||
{
|
||||
uint result = BottomBits & 1u;
|
||||
Advance(1);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -29,17 +61,11 @@ internal struct BitStream
|
||||
/// </summary>
|
||||
/// <param name="numBits"></param>
|
||||
/// <returns></returns>
|
||||
public int ReadBitsReversed(int numBits)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public uint ReadBitsReversed(int numBits)
|
||||
{
|
||||
int bits = this.ReadBits(numBits);
|
||||
uint bits = this.ReadBits(numBits);
|
||||
// Reverse the bits.
|
||||
int result = 0;
|
||||
while (numBits-- != 0)
|
||||
{
|
||||
result <<= 1;
|
||||
result |= bits & 1;
|
||||
bits >>= 1;
|
||||
}
|
||||
return result;
|
||||
return bits.ReverseBits() >> (32 - numBits);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
internal static class NumericExtensions
|
||||
{
|
||||
public static uint ReverseBits(this uint n)
|
||||
{
|
||||
n = (n >> 1) & 0x55555555 | (n << 1) & 0xAAAAAAAA;
|
||||
n = (n >> 2) & 0x33333333 | (n << 2) & 0xCCCCCCCC;
|
||||
n = (n >> 4) & 0x0F0F0F0F | (n << 4) & 0xF0F0F0F0;
|
||||
n = (n >> 8) & 0x00FF00FF | (n << 8) & 0xFF00FF00;
|
||||
n = (n >> 16) & 0x0000FFFF | (n << 16) & 0xFFFF0000;
|
||||
return n;
|
||||
}
|
||||
|
||||
public static uint ToUnsigned(this int value)
|
||||
{
|
||||
return unchecked((uint)value);
|
||||
}
|
||||
|
||||
public static int ToSigned(this uint value)
|
||||
{
|
||||
return unchecked((int)value);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Dxt
|
||||
{
|
||||
@@ -8,66 +9,92 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </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">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>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</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)
|
||||
public static int DecompressDXT1<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 * sizeof(uint)];
|
||||
return DecompressDXT1(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressDXT1<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </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">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>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</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)
|
||||
public static int DecompressDXT1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output, width, height);
|
||||
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressDXT1<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </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">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 width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1<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 offset = 0;
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
uint[] buffer = new uint[16];
|
||||
int[] colors = new int[4];
|
||||
Span<TOutputColor> buffer = stackalloc TOutputColor[16];
|
||||
Span<TOutputColor> colors = stackalloc TOutputColor[4];
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, offset += 8)
|
||||
{
|
||||
int q0 = input[offset + 0] | input[offset + 1] << 8;
|
||||
int q1 = input[offset + 2] | input[offset + 3] << 8;
|
||||
Rgb565(q0, out int r0, out int g0, out int b0);
|
||||
Rgb565(q1, out int r1, out int g1, out int b1);
|
||||
colors[0] = Color(r0, g0, b0, 255);
|
||||
colors[1] = Color(r1, g1, b1, 255);
|
||||
Rgb565(q0, out byte r0, out byte g0, out byte b0);
|
||||
Rgb565(q1, out byte r1, out byte g1, out byte b1);
|
||||
colors[0].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(r0, g0, b0);
|
||||
colors[1].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(r1, g1, b1);
|
||||
if (q0 > q1)
|
||||
{
|
||||
colors[2] = Color((r0 * 2 + r1) / 3, (g0 * 2 + g1) / 3, (b0 * 2 + b1) / 3, 255);
|
||||
colors[3] = Color((r0 + r1 * 2) / 3, (g0 + g1 * 2) / 3, (b0 + b1 * 2) / 3, 255);
|
||||
colors[2].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>((byte)((r0 * 2 + r1) / 3), (byte)((g0 * 2 + g1) / 3), (byte)((b0 * 2 + b1) / 3));
|
||||
colors[3].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>((byte)((r0 + r1 * 2) / 3), (byte)((g0 + g1 * 2) / 3), (byte)((b0 + b1 * 2) / 3));
|
||||
}
|
||||
else
|
||||
{
|
||||
colors[2] = Color((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 255);
|
||||
colors[2].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>((byte)((r0 + r1) / 2), (byte)((g0 + g1) / 2), (byte)((b0 + b1) / 2));
|
||||
colors[3].SetBlack<TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
|
||||
uint d = ToUInt32(input, offset + 4);
|
||||
for (int i = 0; i < 16; i++, d >>= 2)
|
||||
{
|
||||
buffer[i] = unchecked((uint)colors[d & 3]);
|
||||
buffer[i] = colors[unchecked((int)(d & 3))];
|
||||
}
|
||||
|
||||
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
ReadOnlySpan<byte> bufferSpan = MemoryMarshal.Cast<uint, byte>(new ReadOnlySpan<uint>(buffer));
|
||||
BlockCopy(bufferSpan, i * 4 * 4, output, (y * width + s * 4) * 4, clen);
|
||||
BlockCopy(buffer, i * 4, output, y * width + s * 4, clen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,36 +105,62 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
/// <summary>
|
||||
/// Decompress a DXT3 image
|
||||
/// </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">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>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</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)
|
||||
public static int DecompressDXT3<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 * sizeof(uint)];
|
||||
return DecompressDXT3(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressDXT3<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </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">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 width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressDXT3<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT3 image
|
||||
/// </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">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>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</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)
|
||||
public static int DecompressDXT3<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output, width, height);
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
|
||||
int offset = 0;
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
uint[] buffer = new uint[16];
|
||||
int[] colors = new int[4];
|
||||
int[] alphas = new int[16];
|
||||
Span<TOutputColor> buffer = stackalloc TOutputColor[16];
|
||||
Span<ColorRGB<byte>> colors = stackalloc ColorRGB<byte>[4];
|
||||
Span<byte> alphas = stackalloc byte[16];
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, offset += 16)
|
||||
@@ -115,39 +168,39 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int alpha = input[offset + i * 2] | input[offset + i * 2 + 1] << 8;
|
||||
alphas[i * 4 + 0] = (((alpha >> 0) & 0xF) * 0x11) << 24;
|
||||
alphas[i * 4 + 1] = (((alpha >> 4) & 0xF) * 0x11) << 24;
|
||||
alphas[i * 4 + 2] = (((alpha >> 8) & 0xF) * 0x11) << 24;
|
||||
alphas[i * 4 + 3] = (((alpha >> 12) & 0xF) * 0x11) << 24;
|
||||
alphas[i * 4 + 0] = (byte)(((alpha >> 0) & 0xF) * 0x11);
|
||||
alphas[i * 4 + 1] = (byte)(((alpha >> 4) & 0xF) * 0x11);
|
||||
alphas[i * 4 + 2] = (byte)(((alpha >> 8) & 0xF) * 0x11);
|
||||
alphas[i * 4 + 3] = (byte)(((alpha >> 12) & 0xF) * 0x11);
|
||||
}
|
||||
|
||||
int q0 = input[offset + 8] | input[offset + 9] << 8;
|
||||
int q1 = input[offset + 10] | input[offset + 11] << 8;
|
||||
Rgb565(q0, out int r0, out int g0, out int b0);
|
||||
Rgb565(q1, out int r1, out int g1, out int b1);
|
||||
colors[0] = Color(r0, g0, b0, 0);
|
||||
colors[1] = Color(r1, g1, b1, 0);
|
||||
Rgb565(q0, out byte r0, out byte g0, out byte b0);
|
||||
Rgb565(q1, out byte r1, out byte g1, out byte b1);
|
||||
colors[0] = new ColorRGB<byte>(r0, g0, b0);
|
||||
colors[1] = new ColorRGB<byte>(r1, g1, b1);
|
||||
if (q0 > q1)
|
||||
{
|
||||
colors[2] = Color((r0 * 2 + r1) / 3, (g0 * 2 + g1) / 3, (b0 * 2 + b1) / 3, 0);
|
||||
colors[3] = Color((r0 + r1 * 2) / 3, (g0 + g1 * 2) / 3, (b0 + b1 * 2) / 3, 0);
|
||||
colors[2] = new ColorRGB<byte>((byte)((r0 * 2 + r1) / 3), (byte)((g0 * 2 + g1) / 3), (byte)((b0 * 2 + b1) / 3));
|
||||
colors[3] = new ColorRGB<byte>((byte)((r0 + r1 * 2) / 3), (byte)((g0 + g1 * 2) / 3), (byte)((b0 + b1 * 2) / 3));
|
||||
}
|
||||
else
|
||||
{
|
||||
colors[2] = Color((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 0);
|
||||
colors[2] = new ColorRGB<byte>((byte)((r0 + r1) / 2), (byte)((g0 + g1) / 2), (byte)((b0 + b1) / 2));
|
||||
colors[3].SetBlack<ColorRGB<byte>, byte>();
|
||||
}
|
||||
|
||||
uint d = ToUInt32(input, offset + 12);
|
||||
for (int i = 0; i < 16; i++, d >>= 2)
|
||||
{
|
||||
buffer[i] = unchecked((uint)(colors[d & 3] | alphas[i]));
|
||||
buffer[i].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(colors[unchecked((int)(d & 3))], alphas[i]);
|
||||
}
|
||||
|
||||
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
ReadOnlySpan<byte> bufferSpan = MemoryMarshal.Cast<uint, byte>(new ReadOnlySpan<uint>(buffer));
|
||||
BlockCopy(bufferSpan, i * 4 * 4, output, (y * width + s * 4) * 4, clen);
|
||||
BlockCopy(buffer, i * 4, output, y * width + s * 4, clen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,126 +211,145 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
/// <summary>
|
||||
/// Decompress a DXT5 image
|
||||
/// </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">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>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</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)
|
||||
public static int DecompressDXT5<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 * sizeof(uint)];
|
||||
return DecompressDXT5(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressDXT5<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </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">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 width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressDXT5<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT5 image
|
||||
/// </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">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>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</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)
|
||||
public static int DecompressDXT5<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output, width, height);
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
|
||||
int offset = 0;
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
uint[] buffer = new uint[16];
|
||||
int[] colors = new int[4];
|
||||
int[] alphas = new int[8];
|
||||
Span<TOutputColor> buffer = stackalloc TOutputColor[16];
|
||||
Span<ColorRGB<byte>> colors = stackalloc ColorRGB<byte>[4];
|
||||
Span<byte> alphas = stackalloc byte[8];
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, offset += 16)
|
||||
{
|
||||
alphas[0] = input[offset + 0];
|
||||
alphas[1] = input[offset + 1];
|
||||
if (alphas[0] > alphas[1])
|
||||
byte a0 = input[offset + 0];
|
||||
byte a1 = input[offset + 1];
|
||||
alphas[0] = a0;
|
||||
alphas[1] = a1;
|
||||
if (a0 > a1)
|
||||
{
|
||||
alphas[2] = (alphas[0] * 6 + alphas[1]) / 7;
|
||||
alphas[3] = (alphas[0] * 5 + alphas[1] * 2) / 7;
|
||||
alphas[4] = (alphas[0] * 4 + alphas[1] * 3) / 7;
|
||||
alphas[5] = (alphas[0] * 3 + alphas[1] * 4) / 7;
|
||||
alphas[6] = (alphas[0] * 2 + alphas[1] * 5) / 7;
|
||||
alphas[7] = (alphas[0] + alphas[1] * 6) / 7;
|
||||
alphas[2] = (byte)((a0 * 6 + a1) / 7);
|
||||
alphas[3] = (byte)((a0 * 5 + a1 * 2) / 7);
|
||||
alphas[4] = (byte)((a0 * 4 + a1 * 3) / 7);
|
||||
alphas[5] = (byte)((a0 * 3 + a1 * 4) / 7);
|
||||
alphas[6] = (byte)((a0 * 2 + a1 * 5) / 7);
|
||||
alphas[7] = (byte)((a0 + a1 * 6) / 7);
|
||||
}
|
||||
else
|
||||
{
|
||||
alphas[2] = (alphas[0] * 4 + alphas[1]) / 5;
|
||||
alphas[3] = (alphas[0] * 3 + alphas[1] * 2) / 5;
|
||||
alphas[4] = (alphas[0] * 2 + alphas[1] * 3) / 5;
|
||||
alphas[5] = (alphas[0] + alphas[1] * 4) / 5;
|
||||
alphas[7] = 255;
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
alphas[i] <<= 24;
|
||||
alphas[2] = (byte)((a0 * 4 + a1) / 5);
|
||||
alphas[3] = (byte)((a0 * 3 + a1 * 2) / 5);
|
||||
alphas[4] = (byte)((a0 * 2 + a1 * 3) / 5);
|
||||
alphas[5] = (byte)((a0 + a1 * 4) / 5);
|
||||
alphas[6] = byte.MinValue;
|
||||
alphas[7] = byte.MaxValue;
|
||||
}
|
||||
|
||||
int q0 = input[offset + 8] | input[offset + 9] << 8;
|
||||
int q1 = input[offset + 10] | input[offset + 11] << 8;
|
||||
Rgb565(q0, out int r0, out int g0, out int b0);
|
||||
Rgb565(q1, out int r1, out int g1, out int b1);
|
||||
colors[0] = Color(r0, g0, b0, 0);
|
||||
colors[1] = Color(r1, g1, b1, 0);
|
||||
Rgb565(q0, out byte r0, out byte g0, out byte b0);
|
||||
Rgb565(q1, out byte r1, out byte g1, out byte b1);
|
||||
colors[0] = new ColorRGB<byte>(r0, g0, b0);
|
||||
colors[1] = new ColorRGB<byte>(r1, g1, b1);
|
||||
if (q0 > q1)
|
||||
{
|
||||
colors[2] = Color((r0 * 2 + r1) / 3, (g0 * 2 + g1) / 3, (b0 * 2 + b1) / 3, 0);
|
||||
colors[3] = Color((r0 + r1 * 2) / 3, (g0 + g1 * 2) / 3, (b0 + b1 * 2) / 3, 0);
|
||||
colors[2] = new ColorRGB<byte>((byte)((r0 * 2 + r1) / 3), (byte)((g0 * 2 + g1) / 3), (byte)((b0 * 2 + b1) / 3));
|
||||
colors[3] = new ColorRGB<byte>((byte)((r0 + r1 * 2) / 3), (byte)((g0 + g1 * 2) / 3), (byte)((b0 + b1 * 2) / 3));
|
||||
}
|
||||
else
|
||||
{
|
||||
colors[2] = Color((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 0);
|
||||
colors[2] = new ColorRGB<byte>((byte)((r0 + r1) / 2), (byte)((g0 + g1) / 2), (byte)((b0 + b1) / 2));
|
||||
colors[3].SetBlack<ColorRGB<byte>, byte>();
|
||||
}
|
||||
|
||||
ulong da = ToUInt64(input, offset) >> 16;
|
||||
uint dc = ToUInt32(input, offset + 12);
|
||||
for (int i = 0; i < 16; i++, da >>= 3, dc >>= 2)
|
||||
{
|
||||
buffer[i] = unchecked((uint)(alphas[da & 7] | colors[dc & 3]));
|
||||
buffer[i].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(colors[unchecked((int)(dc & 3))], alphas[unchecked((int)(da & 7))]);
|
||||
}
|
||||
|
||||
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
ReadOnlySpan<byte> bufferSpan = MemoryMarshal.Cast<uint, byte>(new ReadOnlySpan<uint>(buffer));
|
||||
BlockCopy(bufferSpan, i * 4 * 4, output, (y * width + s * 4) * 4, clen);
|
||||
BlockCopy(buffer, i * 4, output, y * width + s * 4, clen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static void Rgb565(int c, out int r, out int g, out int b)
|
||||
private static void Rgb565(int c, out byte r, out byte g, out byte b)
|
||||
{
|
||||
r = (c & 0xf800) >> 8;
|
||||
g = (c & 0x07e0) >> 3;
|
||||
b = (c & 0x001f) << 3;
|
||||
r |= r >> 5;
|
||||
g |= g >> 6;
|
||||
b |= b >> 5;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static int Color(int r, int g, int b, int a)
|
||||
{
|
||||
return r << 16 | g << 8 | b | a << 24;
|
||||
r = unchecked((byte)((c & 0xf800) >> 8));
|
||||
g = unchecked((byte)((c & 0x07e0) >> 3));
|
||||
b = unchecked((byte)((c & 0x001f) << 3));
|
||||
r |= (byte)(r >> 5);
|
||||
g |= (byte)(g >> 6);
|
||||
b |= (byte)(b >> 5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Based on <see cref="Buffer.BlockCopy(Array, int, Array, int, int)"/>
|
||||
/// </summary>
|
||||
/// <param name="source">The source buffer.</param>
|
||||
/// <param name="sourceOffset">The zero-based byte offset into source.</param>
|
||||
/// <param name="sourceOffset">The zero-based offset into source.</param>
|
||||
/// <param name="destination">The destination buffer.</param>
|
||||
/// <param name="destinationOffset">The zero-based byte offset into destination.</param>
|
||||
/// <param name="count">The number of bytes to copy.</param>
|
||||
/// <param name="destinationOffset">The zero-based offset into destination.</param>
|
||||
/// <param name="count">The number of items to copy.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static void BlockCopy(ReadOnlySpan<byte> source, int sourceOffset, Span<byte> destination, int destinationOffset, int count)
|
||||
private static void BlockCopy<T>(ReadOnlySpan<T> source, int sourceOffset, Span<T> destination, int destinationOffset, int count)
|
||||
{
|
||||
source.Slice(sourceOffset, count).CopyTo(destination.Slice(destinationOffset));
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,112 @@
|
||||
using System.Buffers;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
internal static class BinaryWriterExtensions
|
||||
{
|
||||
public static void WriteLengthPrefixedString(this BinaryWriter writer, string value)
|
||||
{
|
||||
int length = Encoding.UTF8.GetByteCount(value);
|
||||
writer.Write(length);
|
||||
|
||||
byte[]? rentedArray;
|
||||
scoped Span<byte> buffer;
|
||||
if (length <= 1024)
|
||||
{
|
||||
rentedArray = null;
|
||||
buffer = stackalloc byte[length];
|
||||
}
|
||||
else
|
||||
{
|
||||
rentedArray = ArrayPool<byte>.Shared.Rent(length);
|
||||
buffer = rentedArray.AsSpan(0, length);
|
||||
}
|
||||
|
||||
Encoding.UTF8.GetBytes(value, buffer);
|
||||
writer.Write(buffer);
|
||||
|
||||
if (rentedArray is not null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(rentedArray);
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteNullTerminatedString(this BinaryWriter writer, string value)
|
||||
{
|
||||
int length = Encoding.UTF8.GetByteCount(value);
|
||||
|
||||
byte[]? rentedArray;
|
||||
scoped Span<byte> buffer;
|
||||
if (length <= 1024)
|
||||
{
|
||||
rentedArray = null;
|
||||
buffer = stackalloc byte[length];
|
||||
}
|
||||
else
|
||||
{
|
||||
rentedArray = ArrayPool<byte>.Shared.Rent(length);
|
||||
buffer = rentedArray.AsSpan(0, length);
|
||||
}
|
||||
|
||||
Encoding.UTF8.GetBytes(value, buffer);
|
||||
writer.Write(buffer);
|
||||
|
||||
if (rentedArray is not null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(rentedArray);
|
||||
}
|
||||
|
||||
writer.WriteNullByte();
|
||||
}
|
||||
|
||||
public static void WriteNullTerminatedString(this BinaryWriter writer, ReadOnlySpan<byte> utf8String)
|
||||
{
|
||||
writer.Write(utf8String);
|
||||
writer.WriteNullByte();
|
||||
}
|
||||
|
||||
public static void WriteNullByte(this BinaryWriter writer)
|
||||
{
|
||||
writer.Write(default(byte));
|
||||
}
|
||||
|
||||
public static void Write<T>(this BinaryWriter writer, T value) where T : unmanaged, Enum
|
||||
{
|
||||
switch (Unsafe.SizeOf<T>())
|
||||
{
|
||||
case sizeof(byte):
|
||||
writer.Write(Unsafe.BitCast<T, byte>(value));
|
||||
break;
|
||||
case sizeof(ushort):
|
||||
writer.Write(Unsafe.BitCast<T, ushort>(value));
|
||||
break;
|
||||
case sizeof(uint):
|
||||
writer.Write(Unsafe.BitCast<T, uint>(value));
|
||||
break;
|
||||
case sizeof(ulong):
|
||||
writer.Write(Unsafe.BitCast<T, ulong>(value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteAttribute<T>(this BinaryWriter writer, ReadOnlySpan<byte> attributeName, T attributeValue) where T : IExrDataType
|
||||
{
|
||||
writer.Write(attributeName);
|
||||
writer.WriteNullByte();
|
||||
writer.Write(T.TypeName);
|
||||
writer.WriteNullByte();
|
||||
writer.Write(attributeValue.Size);
|
||||
attributeValue.Write(writer);
|
||||
}
|
||||
|
||||
public static void WriteAttribute<T>(this BinaryWriter writer, ReadOnlySpan<byte> attributeName, ReadOnlySpan<byte> attributeType, T attributeValue) where T : unmanaged, Enum
|
||||
{
|
||||
writer.Write(attributeName);
|
||||
writer.WriteNullByte();
|
||||
writer.Write(attributeType);
|
||||
writer.WriteNullByte();
|
||||
writer.Write(Unsafe.SizeOf<T>());
|
||||
writer.Write(attributeValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public static class ExrAttributes
|
||||
{
|
||||
public static ReadOnlySpan<byte> Channels => "channels"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> Compression => "compression"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> DataWindow => "dataWindow"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> DisplayWindow => "displayWindow"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> LineOrder => "lineOrder"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> PixelAspectRatio => "pixelAspectRatio"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> ScreenWindowCenter => "screenWindowCenter"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> ScreenWindowWidth => "screenWindowWidth"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> Tiles => "tiles"u8;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrBox2F(float XMin, float YMin, float XMax, float YMax) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "box2f"u8;
|
||||
|
||||
public int Size => sizeof(float) + sizeof(float) + sizeof(float) + sizeof(float);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(XMin);
|
||||
writer.Write(YMin);
|
||||
writer.Write(XMax);
|
||||
writer.Write(YMax);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrBox2I(int XMin, int YMin, int XMax, int YMax) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "box2i"u8;
|
||||
|
||||
public int Size => sizeof(int) + sizeof(int) + sizeof(int) + sizeof(int);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(XMin);
|
||||
writer.Write(YMin);
|
||||
writer.Write(XMax);
|
||||
writer.Write(YMax);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Text;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrChannel(string Name, ExrPixelType PixelType, bool PLinear, int XSampling, int YSampling)
|
||||
{
|
||||
public int Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return Encoding.UTF8.GetByteCount(Name)
|
||||
+ sizeof(byte)//Null terminator
|
||||
+ sizeof(ExrPixelType)
|
||||
+ sizeof(bool)
|
||||
+ 3 * sizeof(byte)//Reserved bytes
|
||||
+ sizeof(int)
|
||||
+ sizeof(int);
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.WriteNullTerminatedString(Name);
|
||||
writer.Write(PixelType);
|
||||
writer.Write(PLinear);
|
||||
writer.WriteNullByte();
|
||||
writer.WriteNullByte();
|
||||
writer.WriteNullByte();
|
||||
writer.Write(XSampling);
|
||||
writer.Write(YSampling);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrChannelList(ExrChannel[] Channels) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "chlist"u8;
|
||||
|
||||
public int Size => Channels.Sum(c => c.Size);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
foreach (ExrChannel channel in Channels)
|
||||
{
|
||||
channel.Write(writer);
|
||||
}
|
||||
writer.WriteNullByte();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public enum ExrCompression : byte
|
||||
{
|
||||
None = 0,
|
||||
RLE = 1,
|
||||
ZIPS = 2,
|
||||
ZIP = 3,
|
||||
PIZ = 4,
|
||||
PXR24 = 5,
|
||||
B44 = 6,
|
||||
B44A = 7,
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrDouble(double Value) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "double"u8;
|
||||
|
||||
public int Size => sizeof(double);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Value);
|
||||
}
|
||||
|
||||
public static implicit operator double(ExrDouble value) => value.Value;
|
||||
|
||||
public static implicit operator ExrDouble(double value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
public enum ExrImageFormat
|
||||
{
|
||||
ScanLines,
|
||||
Tiles,
|
||||
}
|
||||
public static class ExrImageFormatExtensions
|
||||
{
|
||||
public static byte ToVersionByte(this ExrImageFormat format) => format switch
|
||||
{
|
||||
ExrImageFormat.Tiles => 0x02,
|
||||
_ => 0x00,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrInt32(int Value) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "int"u8;
|
||||
|
||||
public int Size => sizeof(int);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Value);
|
||||
}
|
||||
|
||||
public static implicit operator int(ExrInt32 value) => value.Value;
|
||||
|
||||
public static implicit operator ExrInt32(int value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
[Flags]
|
||||
public enum ExrLevelMode : byte
|
||||
{
|
||||
OneLevel = 0,
|
||||
MipMapLevels = 1,
|
||||
RipMapLevels = 2,
|
||||
RoundDown = 0,
|
||||
RoundUp = 16,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public enum ExrLineOrder : byte
|
||||
{
|
||||
IncreasingY = 0,
|
||||
DecreasingY = 1,
|
||||
RandomY = 2,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public enum ExrPixelType : int
|
||||
{
|
||||
UInt = 0,
|
||||
Half = 1,
|
||||
Single = 2,
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrSingle(float Value) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "float"u8;
|
||||
|
||||
public int Size => sizeof(float);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Value);
|
||||
}
|
||||
|
||||
public static implicit operator float(ExrSingle value) => value.Value;
|
||||
|
||||
public static implicit operator ExrSingle(float value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Text;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrString(string Value) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "string"u8;
|
||||
|
||||
public int Size => sizeof(int) + Encoding.UTF8.GetByteCount(Value);
|
||||
|
||||
public void Write(BinaryWriter writer) => writer.WriteLengthPrefixedString(Value);
|
||||
|
||||
public static implicit operator string(ExrString value) => value.Value;
|
||||
|
||||
public static implicit operator ExrString(string value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrVector2(Vector2 Value) : IExrDataType
|
||||
{
|
||||
public ExrVector2(float x, float y) : this(new Vector2(x, y))
|
||||
{
|
||||
}
|
||||
|
||||
public int Size => sizeof(float) + sizeof(float);
|
||||
|
||||
public float X => Value.X;
|
||||
|
||||
public float Y => Value.Y;
|
||||
|
||||
public static ReadOnlySpan<byte> TypeName => "v2f"u8;
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(X);
|
||||
writer.Write(Y);
|
||||
}
|
||||
|
||||
public static implicit operator Vector2(ExrVector2 value) => value.Value;
|
||||
|
||||
public static implicit operator ExrVector2(Vector2 value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public static class ExrWriter
|
||||
{
|
||||
public static ReadOnlySpan<byte> Magic => [0x76, 0x2f, 0x31, 0x01];
|
||||
|
||||
public static void Write<TColor, TChannelValue>(Stream stream, int width, int height, ReadOnlySpan<TColor> pixels)
|
||||
where TChannelValue : unmanaged
|
||||
where TColor : unmanaged, IColor<TChannelValue>
|
||||
{
|
||||
int ChannelCount = Color.GetChannelCount<TColor>();
|
||||
ExrPixelType PixelType = Unsafe.SizeOf<TChannelValue>() <= sizeof(ushort) ? ExrPixelType.Half : ExrPixelType.Single;
|
||||
int ChannelSize = PixelType is ExrPixelType.Half ? Unsafe.SizeOf<Half>() : sizeof(float);
|
||||
|
||||
using BinaryWriter writer = new BinaryWriter(stream);
|
||||
|
||||
//Magic bytes
|
||||
writer.Write(Magic);
|
||||
|
||||
//Version bytes
|
||||
writer.Write((byte)2);//OpenEXR version 2
|
||||
writer.Write(ExrImageFormat.ScanLines.ToVersionByte());
|
||||
writer.WriteNullByte();
|
||||
writer.WriteNullByte();
|
||||
|
||||
ExrChannel[] channels;
|
||||
{
|
||||
Span<ExrChannel> buffer = [default, default, default, default];
|
||||
//Stackalloc doesn't work for managed types.
|
||||
//This can be reworked when ref structs can be used in generics.
|
||||
|
||||
int i = 0;
|
||||
|
||||
//The order doesn't really matter here because the format sorts channels alphabetically
|
||||
//when reading reading scan lines, but this makes that more clear.
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
buffer[i] = new ExrChannel("A", PixelType, false, 1, 1);
|
||||
i++;
|
||||
}
|
||||
if (TColor.HasBlueChannel)
|
||||
{
|
||||
buffer[i] = new ExrChannel("B", PixelType, false, 1, 1);
|
||||
i++;
|
||||
}
|
||||
if (TColor.HasGreenChannel)
|
||||
{
|
||||
buffer[i] = new ExrChannel("G", PixelType, false, 1, 1);
|
||||
i++;
|
||||
}
|
||||
if (TColor.HasRedChannel)
|
||||
{
|
||||
buffer[i] = new ExrChannel("R", PixelType, false, 1, 1);
|
||||
i++;
|
||||
}
|
||||
|
||||
channels = buffer[..i].ToArray();
|
||||
}
|
||||
|
||||
//Attributes
|
||||
writer.WriteAttribute(ExrAttributes.Channels, new ExrChannelList(channels));
|
||||
writer.WriteAttribute(ExrAttributes.Compression, "compression"u8, ExrCompression.None);
|
||||
writer.WriteAttribute(ExrAttributes.DataWindow, new ExrBox2I(0, 0, width - 1, height - 1));
|
||||
writer.WriteAttribute(ExrAttributes.DisplayWindow, new ExrBox2I(0, 0, width - 1, height - 1));
|
||||
writer.WriteAttribute(ExrAttributes.LineOrder, "lineOrder"u8, ExrLineOrder.IncreasingY);
|
||||
writer.WriteAttribute(ExrAttributes.PixelAspectRatio, new ExrSingle(1f));
|
||||
writer.WriteAttribute(ExrAttributes.ScreenWindowCenter, new ExrVector2(0f, 0f));
|
||||
writer.WriteAttribute(ExrAttributes.ScreenWindowWidth, new ExrSingle(1f));
|
||||
writer.WriteNullByte();//End of header
|
||||
|
||||
//Scan line offset table
|
||||
long offset = writer.BaseStream.Position + height * sizeof(ulong);
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
writer.Write(offset);
|
||||
offset += sizeof(int) + ChannelCount * ChannelSize * width;
|
||||
}
|
||||
|
||||
//Scan lines
|
||||
if (PixelType is ExrPixelType.Half)
|
||||
{
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
writer.Write(i);
|
||||
writer.Write(ChannelCount * ChannelSize * width);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
Half alpha = NumericConversion.Convert<TChannelValue, Half>(pixels[i * width + j].A);
|
||||
writer.Write(alpha);
|
||||
}
|
||||
}
|
||||
if (TColor.HasBlueChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float blue = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].B);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write((Half)ConvertSRGB(blue * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((Half)ConvertSRGB(blue));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TColor.HasGreenChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float green = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].G);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write((Half)ConvertSRGB(green * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((Half)ConvertSRGB(green));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TColor.HasRedChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float red = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].R);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write((Half)ConvertSRGB(red * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((Half)ConvertSRGB(red));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
writer.Write(i);
|
||||
writer.Write(ChannelCount * ChannelSize * width);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write(alpha);
|
||||
}
|
||||
}
|
||||
if (TColor.HasBlueChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float blue = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].B);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write(ConvertSRGB(blue * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(ConvertSRGB(blue));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TColor.HasGreenChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float green = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].G);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write(ConvertSRGB(green * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(ConvertSRGB(green));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TColor.HasRedChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float red = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].R);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write(ConvertSRGB(red * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(ConvertSRGB(red));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float ConvertSRGB(float value)
|
||||
{
|
||||
//https://en.wikipedia.org/wiki/SRGB#From_sRGB_to_CIE_XYZ
|
||||
//https://en.wikipedia.org/wiki/Gamma_correction#Computer_displays
|
||||
if (value < 0.04045)
|
||||
{
|
||||
return value / 12.92f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return float.Pow((value + 0.055f) / 1.055f, 2.4f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public interface IExrDataType
|
||||
{
|
||||
static abstract ReadOnlySpan<byte> TypeName { get; }
|
||||
|
||||
int Size { get; }
|
||||
|
||||
void Write(BinaryWriter writer);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct TileDescription(int XSize, int YSize, ExrLevelMode Mode) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "tileDesc"u8;
|
||||
|
||||
public int Size => sizeof(int) + sizeof(int) + sizeof(ExrLevelMode);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(XSize);
|
||||
writer.Write(YSize);
|
||||
writer.Write(Mode);
|
||||
}
|
||||
}
|
||||
@@ -2,3 +2,4 @@
|
||||
global using System.Diagnostics.CodeAnalysis;
|
||||
global using System.Numerics;
|
||||
global using System.Runtime.CompilerServices;
|
||||
global using System.Runtime.InteropServices;
|
||||
@@ -1,25 +1,11 @@
|
||||
//#define DISABLE_TWINDDLING_ROUTINE
|
||||
#define ASSUME_IMAGE_TILING
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc;
|
||||
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
public static partial class PvrtcDecoder
|
||||
{
|
||||
public static partial class PvrtcDecoder
|
||||
/// <summary>
|
||||
/// 8 bytes (64 bits) per block
|
||||
/// </summary>
|
||||
private readonly record struct AmtcBlock(uint PackedData0, uint PackedData1)
|
||||
{
|
||||
/// <summary>
|
||||
/// 8 bytes
|
||||
/// </summary>
|
||||
private struct AmtcBlock
|
||||
{
|
||||
public AmtcBlock(uint v0, uint v1)
|
||||
{
|
||||
PackedData0 = v0;
|
||||
PackedData1 = v1;
|
||||
}
|
||||
|
||||
// Uses 64 bits pre block
|
||||
public readonly uint PackedData0;
|
||||
public readonly uint PackedData1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,16 @@
|
||||
//#define DISABLE_TWINDDLING_ROUTINE
|
||||
#define ASSUME_IMAGE_TILING
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc;
|
||||
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
public static partial class PvrtcDecoder
|
||||
{
|
||||
public static partial class PvrtcDecoder
|
||||
/// <summary>
|
||||
/// Low precision colours extracted from the blocks
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 2 * 4 integers
|
||||
/// </remarks>
|
||||
[InlineArray(8)]
|
||||
private struct Colours5554
|
||||
{
|
||||
/// <summary>
|
||||
/// Low precision colours extracted from the blocks
|
||||
/// </summary>
|
||||
private unsafe struct Colours5554
|
||||
{
|
||||
public fixed int Reps[2 * 4];
|
||||
}
|
||||
private int _element0;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,65 @@
|
||||
//#define DISABLE_TWINDDLING_ROUTINE
|
||||
#define ASSUME_IMAGE_TILING
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
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);
|
||||
@@ -67,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);
|
||||
@@ -85,16 +110,16 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
if (changed)
|
||||
{
|
||||
AmtcBlock block00 = blocks[(int)blocki00];
|
||||
Unpack5554Colour(block00, m_colors.GetIntSpanForColor(0));
|
||||
Unpack5554Colour(block00, m_colors[0]);
|
||||
UnpackModulations(block00, do2bitMode, modulationVals, modulationModes, 0, 0);
|
||||
AmtcBlock block01 = blocks[(int)blocki01];
|
||||
Unpack5554Colour(block01, m_colors.GetIntSpanForColor(1));
|
||||
Unpack5554Colour(block01, m_colors[1]);
|
||||
UnpackModulations(block01, do2bitMode, modulationVals, modulationModes, xBlockSize, 0);
|
||||
AmtcBlock block10 = blocks[(int)blocki10];
|
||||
Unpack5554Colour(block10, m_colors.GetIntSpanForColor(2));
|
||||
Unpack5554Colour(block10, m_colors[2]);
|
||||
UnpackModulations(block10, do2bitMode, modulationVals, modulationModes, 0, BlockYSize);
|
||||
AmtcBlock block11 = blocks[(int)blocki11];
|
||||
Unpack5554Colour(block11, m_colors.GetIntSpanForColor(3));
|
||||
Unpack5554Colour(block11, m_colors[3]);
|
||||
UnpackModulations(block11, do2bitMode, modulationVals, modulationModes, xBlockSize, BlockYSize);
|
||||
|
||||
pblocki00 = blocki00;
|
||||
@@ -106,16 +131,16 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
}
|
||||
|
||||
// decompress the pixel. First compute the interpolated A and B signals
|
||||
InterpolateColours(m_colors.GetIntSpanForColor(0), m_colors.GetIntSpanForColor(1), m_colors.GetIntSpanForColor(2), m_colors.GetIntSpanForColor(3), 0, do2bitMode, x, y, aSig);
|
||||
InterpolateColours(m_colors.GetIntSpanForColor(0), m_colors.GetIntSpanForColor(1), m_colors.GetIntSpanForColor(2), m_colors.GetIntSpanForColor(3), 1, do2bitMode, x, y, bSig);
|
||||
InterpolateColours(m_colors[0], m_colors[1], m_colors[2], m_colors[3], 0, do2bitMode, x, y, aSig);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,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 (!IsPowerOf2((uint)ySize))
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
if (!IsPowerOf2((uint)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;
|
||||
@@ -166,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;
|
||||
@@ -197,7 +204,6 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
maxValue >>= shiftCount;
|
||||
twiddled |= (maxValue << (2 * shiftCount));
|
||||
return twiddled;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -219,36 +225,36 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
int modVal;
|
||||
if (modulationModes[y * 16 + x] == 0)
|
||||
{
|
||||
modVal = m_repVals0[modulationVals[y * 16 + x]];
|
||||
modVal = RepVals0[modulationVals[y * 16 + x]];
|
||||
}
|
||||
else if (do2bitMode)
|
||||
{
|
||||
// if this is a stored value
|
||||
if (((x ^ y) & 1) == 0)
|
||||
{
|
||||
modVal = m_repVals0[modulationVals[y * 16 + x]];
|
||||
modVal = RepVals0[modulationVals[y * 16 + x]];
|
||||
}
|
||||
// else average from the neighbours if H&V interpolation..
|
||||
else if (modulationModes[y * 16 + x] == 1)
|
||||
{
|
||||
modVal = (m_repVals0[modulationVals[(y - 1) * 16 + x]] + m_repVals0[modulationVals[(y + 1) * 16 + x]] +
|
||||
m_repVals0[modulationVals[y * 16 + x - 1]] + m_repVals0[modulationVals[y * 16 + x + 1]] + 2) / 4;
|
||||
modVal = (RepVals0[modulationVals[(y - 1) * 16 + x]] + RepVals0[modulationVals[(y + 1) * 16 + x]] +
|
||||
RepVals0[modulationVals[y * 16 + x - 1]] + RepVals0[modulationVals[y * 16 + x + 1]] + 2) / 4;
|
||||
}
|
||||
// else if H-Only
|
||||
else if (modulationModes[y * 16 + x] == 2)
|
||||
{
|
||||
modVal = (m_repVals0[modulationVals[y * 16 + x - 1]] + m_repVals0[modulationVals[y * 16 + x + 1]] + 1) / 2;
|
||||
modVal = (RepVals0[modulationVals[y * 16 + x - 1]] + RepVals0[modulationVals[y * 16 + x + 1]] + 1) / 2;
|
||||
}
|
||||
// else it's V-Only
|
||||
else
|
||||
{
|
||||
modVal = (m_repVals0[modulationVals[(y - 1) * 16 + x]] + m_repVals0[modulationVals[(y + 1) * 16 + x]] + 1) / 2;
|
||||
modVal = (RepVals0[modulationVals[(y - 1) * 16 + x]] + RepVals0[modulationVals[(y + 1) * 16 + x]] + 1) / 2;
|
||||
}
|
||||
}
|
||||
// else it's 4BPP and PT encoding
|
||||
else
|
||||
{
|
||||
modVal = m_repVals1[modulationVals[y * 16 + x]];
|
||||
modVal = RepVals1[modulationVals[y * 16 + x]];
|
||||
doPT = modulationVals[y * 16 + x] == PTIndex;
|
||||
}
|
||||
|
||||
@@ -292,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++)
|
||||
@@ -310,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>
|
||||
@@ -398,12 +394,14 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
/// </summary>
|
||||
private static void Unpack5554Colour(AmtcBlock block, Span<int> abColors)
|
||||
{
|
||||
Span<uint> rawBits = stackalloc uint[2];
|
||||
// extract A and B
|
||||
// 15 bits (shifted up by one)
|
||||
rawBits[0] = block.PackedData1 & (0xFFFE);
|
||||
// 16 bits
|
||||
rawBits[1] = block.PackedData1 >> 16;
|
||||
ReadOnlySpan<uint> rawBits =
|
||||
[
|
||||
// extract A and B
|
||||
// 15 bits (shifted up by one)
|
||||
block.PackedData1 & (0xFFFE),
|
||||
// 16 bits
|
||||
block.PackedData1 >> 16,
|
||||
];
|
||||
|
||||
// step through both colours
|
||||
for (int i = 0; i < 2; i++)
|
||||
@@ -455,43 +453,21 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static Span<int> GetIntSpanForColor(this Span<Colours5554> colorSpan, int index)
|
||||
{
|
||||
return MemoryMarshal.Cast<Colours5554, int>(colorSpan.Slice(index, 1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that a number is an integer power of two, i.e. 1, 2, 4, 8, ... etc. Returns false for zero
|
||||
/// Define an expression to either wrap or clamp large or small values to the legal coordinate range.
|
||||
/// </summary>
|
||||
/// <param name="input">A number</param>
|
||||
/// <returns>True if the number is an integer power of two, else false</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static bool IsPowerOf2(uint input)
|
||||
private static int LimitCoordinate(int value, int size, bool assumeImageTiling = true)
|
||||
{
|
||||
if (input == 0)
|
||||
if (assumeImageTiling)
|
||||
{
|
||||
return false;
|
||||
// wrap coordinate
|
||||
return value & (size - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// clamp coordinate
|
||||
return int.Clamp(value, 0, size - 1);
|
||||
}
|
||||
|
||||
uint minus1 = input - 1;
|
||||
return ((input | minus1) == (input ^ minus1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Define an expression to either wrap or clamp large or small vals to the legal coordinate range
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static int LimitCoord(int value, int size)
|
||||
{
|
||||
#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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -510,7 +486,7 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
private const int BlockX2bpp = 8;
|
||||
private const int BlockX4bpp = 4;
|
||||
|
||||
private static readonly int[] m_repVals0 = new int[] { 0, 3, 5, 8 };
|
||||
private static readonly int[] m_repVals1 = new int[] { 0, 4, 4, 8 };
|
||||
private static ReadOnlySpan<int> RepVals0 => [0, 3, 5, 8];
|
||||
private static ReadOnlySpan<int> RepVals1 => [0, 4, 4, 8];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace AssetRipper.TextureDecoder;
|
||||
|
||||
public readonly ref struct ReadOnlySpan2D<T>
|
||||
{
|
||||
public int Width { get; }
|
||||
public int Height { get; }
|
||||
public ReadOnlySpan<T> Data { get; }
|
||||
|
||||
public ReadOnlySpan2D(ReadOnlySpan<T> data, int width, int height)
|
||||
{
|
||||
Validate(data, width, height);
|
||||
Data = data;
|
||||
Width = width;
|
||||
Height = height;
|
||||
|
||||
static void Validate(ReadOnlySpan<T> data, int width, int height)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(width);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(height);
|
||||
if (data.Length != width * height)
|
||||
{
|
||||
throw new ArgumentException("Data length does not match width and height.", nameof(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//public ReadOnlySpan<T> this[int y] => Data.Slice(y * Width, Width);
|
||||
|
||||
public T this[int x, int y] => Data[y * Width + x];
|
||||
|
||||
public static explicit operator ReadOnlySpan2D<T>(T[] array) => new(array, array.Length, 1);
|
||||
|
||||
public static explicit operator ReadOnlySpan2D<T>(ReadOnlySpan<T> span) => new(span, span.Length, 1);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct A : IChannel
|
||||
{
|
||||
static bool IChannel.IsAlpha => true;
|
||||
static T IChannel.GetBlack<T>() => NumericConversion.GetMaximumValueSafe<T>();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct B : IChannel
|
||||
{
|
||||
static bool IChannel.IsBlue => true;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public static class Channel
|
||||
{
|
||||
public static bool TryGetRed<TChannel, TValue>(in TValue field, out TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsRed)
|
||||
{
|
||||
value = TChannel.GetRed(field);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetGreen<TChannel, TValue>(in TValue field, out TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsGreen)
|
||||
{
|
||||
value = TChannel.GetGreen(field);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetBlue<TChannel, TValue>(in TValue field, out TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsBlue)
|
||||
{
|
||||
value = TChannel.GetBlue(field);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetAlpha<TChannel, TValue>(in TValue field, out TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsAlpha)
|
||||
{
|
||||
value = TChannel.GetAlpha(field);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIfRed<TChannel, TValue>(ref TValue field, TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsRed)
|
||||
{
|
||||
TChannel.SetRed(ref field, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIfGreen<TChannel, TValue>(ref TValue field, TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsGreen)
|
||||
{
|
||||
TChannel.SetGreen(ref field, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIfBlue<TChannel, TValue>(ref TValue field, TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsBlue)
|
||||
{
|
||||
TChannel.SetBlue(ref field, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIfAlpha<TChannel, TValue>(ref TValue field, TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsAlpha)
|
||||
{
|
||||
TChannel.SetAlpha(ref field, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct G : IChannel
|
||||
{
|
||||
static bool IChannel.IsGreen => true;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct Gray : IChannel
|
||||
{
|
||||
static bool IChannel.IsRed => true;
|
||||
static bool IChannel.IsGreen => true;
|
||||
static bool IChannel.IsBlue => true;
|
||||
static bool IChannel.FullyUtilized => false;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public interface IChannel
|
||||
{
|
||||
static virtual bool IsRed => false;
|
||||
static virtual bool IsGreen => false;
|
||||
static virtual bool IsBlue => false;
|
||||
static virtual bool IsAlpha => false;
|
||||
static virtual bool FullyUtilized => true;
|
||||
|
||||
static virtual T GetBlack<T>() where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<T>();
|
||||
}
|
||||
|
||||
static virtual T GetWhite<T>() where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return NumericConversion.GetMaximumValueSafe<T>();
|
||||
}
|
||||
|
||||
static virtual T GetRed<T>(T field) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return field;
|
||||
}
|
||||
|
||||
static virtual T GetGreen<T>(T field) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return field;
|
||||
}
|
||||
|
||||
static virtual T GetBlue<T>(T field) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return field;
|
||||
}
|
||||
|
||||
static virtual T GetAlpha<T>(T field) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return field;
|
||||
}
|
||||
|
||||
static virtual void SetRed<T>(ref T field, T value) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
field = value;
|
||||
}
|
||||
|
||||
static virtual void SetGreen<T>(ref T field, T value) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
field = value;
|
||||
}
|
||||
|
||||
static virtual void SetBlue<T>(ref T field, T value) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
field = value;
|
||||
}
|
||||
|
||||
static virtual void SetAlpha<T>(ref T field, T value) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
field = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct R : IChannel
|
||||
{
|
||||
static bool IChannel.IsRed => true;
|
||||
}
|
||||
Loaded 100 of 136 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user