mirror of
https://github.com/AssetRipper/AssetRipper.TextureDecoder.git
synced 2026-11-12 18:07:28 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13b4f6ee49 | ||
|
|
2cdd532c31 | ||
|
|
d772c2790d | ||
|
|
0201123b0a | ||
|
|
02531ab941 | ||
|
|
2f8d0b4a8b | ||
|
|
0fb5e24d81 | ||
|
|
ec91662b4a | ||
|
|
03713d8d57 | ||
|
|
3cf2170242 | ||
|
|
0a8bae09cf | ||
|
|
a7a12fdd72 | ||
|
|
801edea4cf | ||
|
|
3cb859c6ac | ||
|
|
0738ba4b50 | ||
|
|
09067cecbb | ||
|
|
ec6754a5d2 | ||
|
|
bc4d0f5378 | ||
|
|
8f0f94695b | ||
|
|
aacbfbd222 | ||
|
|
93fe6948b6 | ||
|
|
0e6f3080be | ||
|
|
f8294eff8b | ||
|
|
0b4d1dfc83 | ||
|
|
f5c1aca0e1 | ||
|
|
d4d335d94b | ||
|
|
70775daca9 | ||
|
|
3a94ea1a0f | ||
|
|
db875125d2 | ||
|
|
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 |
No files matched your search
@@ -12,11 +12,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4.0.0
|
||||
uses: actions/setup-dotnet@v6.0.0
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: 9.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Benchmarks;
|
||||
|
||||
public class Argb32Decoding
|
||||
{
|
||||
private const int Width = 256;//ignoring smaller mips
|
||||
private const int Height = 256;//ignoring smaller mips
|
||||
private readonly byte[] inputData;
|
||||
private readonly byte[] outputData;
|
||||
|
||||
public Argb32Decoding()
|
||||
{
|
||||
inputData = File.ReadAllBytes(PathConstants.PathToTestFiles + "Rgb/" + "test.argb32");
|
||||
outputData = new byte[Width * Height * sizeof(uint)];
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int OriginalMethod() => RgbConverter.ARGB32ToBGRA32(inputData, Width, Height, outputData);
|
||||
|
||||
[Benchmark]
|
||||
public int GenericMethod() => RgbConverter.Convert<ColorARGB32, byte, ColorBGRA32, byte>(inputData, Width, Height, outputData);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AssetRipper.TextureDecoder\AssetRipper.TextureDecoder.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace AssetRipper.TextureDecoder.Benchmarks;
|
||||
|
||||
internal static class PathConstants
|
||||
{
|
||||
internal const string PathToRepositoryRoot = "../../../../../../../../";
|
||||
internal const string PathToTestFiles = PathToRepositoryRoot + "TestFiles/";
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Benchmarks;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
static void Main()
|
||||
{
|
||||
BenchmarkRunner.Run(typeof(Program).Assembly);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Benchmarks;
|
||||
|
||||
public class Rgb9e5Decoding
|
||||
{
|
||||
private const int Width = 256;//ignoring smaller mips
|
||||
private const int Height = 256;//ignoring smaller mips
|
||||
private readonly byte[] inputData;
|
||||
private readonly byte[] outputData;
|
||||
|
||||
public Rgb9e5Decoding()
|
||||
{
|
||||
inputData = File.ReadAllBytes(PathConstants.PathToTestFiles + "Rgb/" + "test.rgb9e5float");
|
||||
outputData = new byte[Width * Height * sizeof(uint)];
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int OriginalMethod() => RgbConverter.RGB9e5FloatToBGRA32(inputData, Width, Height, outputData);
|
||||
|
||||
[Benchmark]
|
||||
public int GenericMethod() => RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA32, byte>(inputData, Width, Height, outputData);
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.1.0" />
|
||||
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -82,14 +82,14 @@ internal static class NumericConversionGenerator
|
||||
writer.WriteLine("if (typeof(TFrom) == typeof(TTo))");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
writer.WriteLine("return Unsafe.As<TFrom, TTo>(ref value);");
|
||||
writer.WriteLine("return Unsafe.BitCast<TFrom, TTo>(value);");
|
||||
}
|
||||
foreach (CSharpPrimitives.Data from in CSharpPrimitives.List)
|
||||
{
|
||||
writer.WriteLine($"else if (typeof(TFrom) == typeof({from.LangName}))");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
writer.WriteLine($"return {ConvertMethodName(from.Type)}<TTo>(Unsafe.As<TFrom, {from.LangName}>(ref value));");
|
||||
writer.WriteLine($"return {ConvertMethodName(from.Type)}<TTo>(Unsafe.BitCast<TFrom, {from.LangName}>(value));");
|
||||
}
|
||||
}
|
||||
writer.WriteLine("else");
|
||||
@@ -133,40 +133,41 @@ internal static class NumericConversionGenerator
|
||||
{
|
||||
if (from == to)
|
||||
{
|
||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref value);");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(value);");
|
||||
}
|
||||
else if (to.HasPointerSize(out CSharpPrimitives.Data? bit32To, out CSharpPrimitives.Data? bit64To))
|
||||
{
|
||||
using (new If(writer, "IntPtr.Size == sizeof(int)"))
|
||||
{
|
||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName}){methodName}<{bit32To.LangName}>(value);");
|
||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||
}
|
||||
using (new Else(writer))
|
||||
{
|
||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName}){methodName}<{bit64To.LangName}>(value);");
|
||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||
}
|
||||
}
|
||||
else if (to.IsSignedInteger(out CSharpPrimitives.Data? unsignedTo))
|
||||
{
|
||||
writer.WriteLine($"{to.LangName} converted = {ChangeSign}({methodName}<{unsignedTo.LangName}>(value));");
|
||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||
}
|
||||
else if (from.IsFloatingPoint)
|
||||
{
|
||||
if (to.IsFloatingPoint)
|
||||
{
|
||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName})value;");
|
||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(to.IsUnsignedInteger);
|
||||
if (from.Type == typeof(Half))
|
||||
CSharpPrimitives.Data conversionType = CSharpPrimitives.GetDataForIntegerFloatingPointConversion(to, from);
|
||||
if (conversionType != from)
|
||||
{
|
||||
writer.WriteComment("We use float because it has enough precision to convert from Half to any integer type.");
|
||||
writer.WriteLine($"return {ConvertMethodName(typeof(float))}<TTo>((float)value);");
|
||||
writer.WriteComment($"We use {conversionType.LangName} because it has enough precision to convert from {from.LangName} to {to.LangName}.");
|
||||
writer.WriteLine($"return {ConvertMethodName(conversionType.Type)}<TTo>(({conversionType.LangName})value);");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -175,7 +176,7 @@ internal static class NumericConversionGenerator
|
||||
string maxValue = to.MaxValue;
|
||||
writer.WriteLine($"{from.LangName} x = value * ({from.LangName}){maxValue};");
|
||||
writer.WriteLine($"{to.LangName} converted = ({from.LangName}){maxValue} < x ? {maxValue} : (x > ({from.LangName}){minValue} ? ({to.LangName})x : {minValue});");
|
||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,17 +185,18 @@ internal static class NumericConversionGenerator
|
||||
Debug.Assert(from.IsUnsignedInteger);
|
||||
if (to.IsFloatingPoint)
|
||||
{
|
||||
if (to.Type == typeof(Half))
|
||||
CSharpPrimitives.Data conversionType = CSharpPrimitives.GetDataForIntegerFloatingPointConversion(from, to);
|
||||
if (conversionType != to)
|
||||
{
|
||||
writer.WriteComment("There isn't enough precision to convert from anything bigger than byte to Half, so we convert to float first.");
|
||||
writer.WriteLine($"float x = {methodName}<float>(value);");
|
||||
writer.WriteLine("Half converted = (Half)x;");
|
||||
writer.WriteLine("return Unsafe.As<Half, TTo>(ref converted);");
|
||||
writer.WriteComment($"There isn't enough precision to convert from {from.LangName} to {to.LangName}, so we convert to {conversionType.LangName} first.");
|
||||
writer.WriteLine($"{conversionType.LangName} x = {methodName}<{conversionType.LangName}>(value);");
|
||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName})x;");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName})value / ({to.LangName}){from.MaxValue};");
|
||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -214,7 +216,7 @@ internal static class NumericConversionGenerator
|
||||
writer.Write($"(({conversionType})value << {i * from.Size * 8}) | ");
|
||||
}
|
||||
writer.WriteLine("value);");
|
||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -225,7 +227,14 @@ internal static class NumericConversionGenerator
|
||||
writer.WriteComment("This is a special case where we already know an optimal algorithm.");
|
||||
writer.WriteLine("uint x = (value * 255u + 32895u) >> 16;");
|
||||
writer.WriteLine("byte converted = unchecked((byte)x);");
|
||||
writer.WriteLine("return Unsafe.As<byte, TTo>(ref converted);");
|
||||
writer.WriteLine("return Unsafe.BitCast<byte, TTo>(converted);");
|
||||
}
|
||||
else if (from.Size < sizeof(double) && to.Size < sizeof(double))
|
||||
{
|
||||
writer.WriteLine($"double interpolated = (double)value / (double){from.MaxValue};");
|
||||
writer.WriteLine($"double exact = interpolated * (double){to.MaxValue};");
|
||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName})double.Round(exact, MidpointRounding.AwayFromZero);");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -236,7 +245,7 @@ internal static class NumericConversionGenerator
|
||||
{
|
||||
int offset = (from.Size / to.Size - 1) * to.Size * 8;
|
||||
writer.WriteLine($"{to.LangName} converted = ({to.LangName})(({conversionType})value >> {offset});");
|
||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{to.LangName}, TTo>(converted);");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,7 +276,7 @@ internal static class NumericConversionGenerator
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
writer.WriteLine($"{from.LangName} value = {methodName}Safe<{from.LangName}>();");
|
||||
writer.WriteLine($"return Unsafe.As<{from.LangName}, T>(ref value);");
|
||||
writer.WriteLine($"return Unsafe.BitCast<{from.LangName}, T>(value);");
|
||||
}
|
||||
}
|
||||
writer.WriteLine("else");
|
||||
|
||||
@@ -1,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,7 +87,7 @@ 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))
|
||||
@@ -129,11 +145,16 @@ internal static partial class Program
|
||||
|
||||
private static void WriteNonGenericStaticProperties(IndentedTextWriter writer, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized, string typeName)
|
||||
{
|
||||
writer.WriteLine($"static bool IColor.HasRedChannel => {hasRed.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColor.HasGreenChannel => {hasGreen.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColor.HasBlueChannel => {hasBlue.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColor.HasAlphaChannel => {hasAlpha.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColor.ChannelsAreFullyUtilized => {fullyUtilized.ToLowerString()};");
|
||||
WriteNonGenericStaticProperties(writer, hasRed.ToLowerString(), hasGreen.ToLowerString(), hasBlue.ToLowerString(), hasAlpha.ToLowerString(), fullyUtilized.ToLowerString(), typeName);
|
||||
}
|
||||
|
||||
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});");
|
||||
}
|
||||
|
||||
@@ -196,7 +217,7 @@ internal static partial class Program
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteGenericColor(IndentedTextWriter writer, string name, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha)
|
||||
private static void WriteGenericColor(IndentedTextWriter writer, string name)
|
||||
{
|
||||
const string typeName = "T";
|
||||
const string minValue = "NumericConversion.GetMinimumValueSafe<T>()";
|
||||
@@ -205,20 +226,45 @@ internal static partial class Program
|
||||
writer.WriteGeneratedCodeWarning();
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteFileScopedNamespace(OutputNamespace);
|
||||
writer.WriteFileScopedNamespace(FormatsNamespace);
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
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();
|
||||
@@ -234,6 +280,165 @@ internal static partial class Program
|
||||
}
|
||||
}
|
||||
|
||||
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}(");
|
||||
|
||||
@@ -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,6 +1,7 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -15,367 +16,495 @@ static partial class Program
|
||||
Console.WriteLine(" 2 - RGB_SByte");
|
||||
Console.WriteLine(" 3 - RGBA_SByte");
|
||||
Console.WriteLine(" 4 - A_SByte");
|
||||
Console.WriteLine(" 5 - R_Byte");
|
||||
Console.WriteLine(" 6 - RG_Byte");
|
||||
Console.WriteLine(" 7 - RGB_Byte");
|
||||
Console.WriteLine(" 8 - RGBA_Byte");
|
||||
Console.WriteLine(" 9 - A_Byte");
|
||||
Console.WriteLine(" 10 - R_Int16");
|
||||
Console.WriteLine(" 11 - RG_Int16");
|
||||
Console.WriteLine(" 12 - RGB_Int16");
|
||||
Console.WriteLine(" 13 - RGBA_Int16");
|
||||
Console.WriteLine(" 14 - A_Int16");
|
||||
Console.WriteLine(" 15 - R_UInt16");
|
||||
Console.WriteLine(" 16 - RG_UInt16");
|
||||
Console.WriteLine(" 17 - RGB_UInt16");
|
||||
Console.WriteLine(" 18 - RGBA_UInt16");
|
||||
Console.WriteLine(" 19 - A_UInt16");
|
||||
Console.WriteLine(" 20 - R_Int32");
|
||||
Console.WriteLine(" 21 - RG_Int32");
|
||||
Console.WriteLine(" 22 - RGB_Int32");
|
||||
Console.WriteLine(" 23 - RGBA_Int32");
|
||||
Console.WriteLine(" 24 - A_Int32");
|
||||
Console.WriteLine(" 25 - R_UInt32");
|
||||
Console.WriteLine(" 26 - RG_UInt32");
|
||||
Console.WriteLine(" 27 - RGB_UInt32");
|
||||
Console.WriteLine(" 28 - RGBA_UInt32");
|
||||
Console.WriteLine(" 29 - A_UInt32");
|
||||
Console.WriteLine(" 30 - R_IntPtr");
|
||||
Console.WriteLine(" 31 - RG_IntPtr");
|
||||
Console.WriteLine(" 32 - RGB_IntPtr");
|
||||
Console.WriteLine(" 33 - RGBA_IntPtr");
|
||||
Console.WriteLine(" 34 - A_IntPtr");
|
||||
Console.WriteLine(" 35 - R_UIntPtr");
|
||||
Console.WriteLine(" 36 - RG_UIntPtr");
|
||||
Console.WriteLine(" 37 - RGB_UIntPtr");
|
||||
Console.WriteLine(" 38 - RGBA_UIntPtr");
|
||||
Console.WriteLine(" 39 - A_UIntPtr");
|
||||
Console.WriteLine(" 40 - R_Int64");
|
||||
Console.WriteLine(" 41 - RG_Int64");
|
||||
Console.WriteLine(" 42 - RGB_Int64");
|
||||
Console.WriteLine(" 43 - RGBA_Int64");
|
||||
Console.WriteLine(" 44 - A_Int64");
|
||||
Console.WriteLine(" 45 - R_UInt64");
|
||||
Console.WriteLine(" 46 - RG_UInt64");
|
||||
Console.WriteLine(" 47 - RGB_UInt64");
|
||||
Console.WriteLine(" 48 - RGBA_UInt64");
|
||||
Console.WriteLine(" 49 - A_UInt64");
|
||||
Console.WriteLine(" 50 - R_Int128");
|
||||
Console.WriteLine(" 51 - RG_Int128");
|
||||
Console.WriteLine(" 52 - RGB_Int128");
|
||||
Console.WriteLine(" 53 - RGBA_Int128");
|
||||
Console.WriteLine(" 54 - A_Int128");
|
||||
Console.WriteLine(" 55 - R_UInt128");
|
||||
Console.WriteLine(" 56 - RG_UInt128");
|
||||
Console.WriteLine(" 57 - RGB_UInt128");
|
||||
Console.WriteLine(" 58 - RGBA_UInt128");
|
||||
Console.WriteLine(" 59 - A_UInt128");
|
||||
Console.WriteLine(" 60 - R_Half");
|
||||
Console.WriteLine(" 61 - RG_Half");
|
||||
Console.WriteLine(" 62 - RGB_Half");
|
||||
Console.WriteLine(" 63 - RGBA_Half");
|
||||
Console.WriteLine(" 64 - A_Half");
|
||||
Console.WriteLine(" 65 - R_Single");
|
||||
Console.WriteLine(" 66 - RG_Single");
|
||||
Console.WriteLine(" 67 - RGB_Single");
|
||||
Console.WriteLine(" 68 - RGBA_Single");
|
||||
Console.WriteLine(" 69 - A_Single");
|
||||
Console.WriteLine(" 70 - R_NFloat");
|
||||
Console.WriteLine(" 71 - RG_NFloat");
|
||||
Console.WriteLine(" 72 - RGB_NFloat");
|
||||
Console.WriteLine(" 73 - RGBA_NFloat");
|
||||
Console.WriteLine(" 74 - A_NFloat");
|
||||
Console.WriteLine(" 75 - R_Double");
|
||||
Console.WriteLine(" 76 - RG_Double");
|
||||
Console.WriteLine(" 77 - RGB_Double");
|
||||
Console.WriteLine(" 78 - RGBA_Double");
|
||||
Console.WriteLine(" 79 - A_Double");
|
||||
Console.WriteLine(" 80 - R_Decimal");
|
||||
Console.WriteLine(" 81 - RG_Decimal");
|
||||
Console.WriteLine(" 82 - RGB_Decimal");
|
||||
Console.WriteLine(" 83 - RGBA_Decimal");
|
||||
Console.WriteLine(" 84 - A_Decimal");
|
||||
Console.WriteLine(" 85 - ARGB16");
|
||||
Console.WriteLine(" 86 - ARGB32");
|
||||
Console.WriteLine(" 87 - BGRA32");
|
||||
Console.WriteLine(" 88 - RGB16");
|
||||
Console.WriteLine(" 89 - RGB9e5");
|
||||
Console.WriteLine(" 90 - RGBA16");
|
||||
Console.WriteLine(" 5 - ARGB_SByte");
|
||||
Console.WriteLine(" 6 - BGRA_SByte");
|
||||
Console.WriteLine(" 7 - R_Byte");
|
||||
Console.WriteLine(" 8 - RG_Byte");
|
||||
Console.WriteLine(" 9 - RGB_Byte");
|
||||
Console.WriteLine(" 10 - RGBA_Byte");
|
||||
Console.WriteLine(" 11 - A_Byte");
|
||||
Console.WriteLine(" 12 - ARGB_Byte");
|
||||
Console.WriteLine(" 13 - BGRA_Byte");
|
||||
Console.WriteLine(" 14 - R_Int16");
|
||||
Console.WriteLine(" 15 - RG_Int16");
|
||||
Console.WriteLine(" 16 - RGB_Int16");
|
||||
Console.WriteLine(" 17 - RGBA_Int16");
|
||||
Console.WriteLine(" 18 - A_Int16");
|
||||
Console.WriteLine(" 19 - ARGB_Int16");
|
||||
Console.WriteLine(" 20 - BGRA_Int16");
|
||||
Console.WriteLine(" 21 - R_UInt16");
|
||||
Console.WriteLine(" 22 - RG_UInt16");
|
||||
Console.WriteLine(" 23 - RGB_UInt16");
|
||||
Console.WriteLine(" 24 - RGBA_UInt16");
|
||||
Console.WriteLine(" 25 - A_UInt16");
|
||||
Console.WriteLine(" 26 - ARGB_UInt16");
|
||||
Console.WriteLine(" 27 - BGRA_UInt16");
|
||||
Console.WriteLine(" 28 - R_Int32");
|
||||
Console.WriteLine(" 29 - RG_Int32");
|
||||
Console.WriteLine(" 30 - RGB_Int32");
|
||||
Console.WriteLine(" 31 - RGBA_Int32");
|
||||
Console.WriteLine(" 32 - A_Int32");
|
||||
Console.WriteLine(" 33 - ARGB_Int32");
|
||||
Console.WriteLine(" 34 - BGRA_Int32");
|
||||
Console.WriteLine(" 35 - R_UInt32");
|
||||
Console.WriteLine(" 36 - RG_UInt32");
|
||||
Console.WriteLine(" 37 - RGB_UInt32");
|
||||
Console.WriteLine(" 38 - RGBA_UInt32");
|
||||
Console.WriteLine(" 39 - A_UInt32");
|
||||
Console.WriteLine(" 40 - ARGB_UInt32");
|
||||
Console.WriteLine(" 41 - BGRA_UInt32");
|
||||
Console.WriteLine(" 42 - R_IntPtr");
|
||||
Console.WriteLine(" 43 - RG_IntPtr");
|
||||
Console.WriteLine(" 44 - RGB_IntPtr");
|
||||
Console.WriteLine(" 45 - RGBA_IntPtr");
|
||||
Console.WriteLine(" 46 - A_IntPtr");
|
||||
Console.WriteLine(" 47 - ARGB_IntPtr");
|
||||
Console.WriteLine(" 48 - BGRA_IntPtr");
|
||||
Console.WriteLine(" 49 - R_UIntPtr");
|
||||
Console.WriteLine(" 50 - RG_UIntPtr");
|
||||
Console.WriteLine(" 51 - RGB_UIntPtr");
|
||||
Console.WriteLine(" 52 - RGBA_UIntPtr");
|
||||
Console.WriteLine(" 53 - A_UIntPtr");
|
||||
Console.WriteLine(" 54 - ARGB_UIntPtr");
|
||||
Console.WriteLine(" 55 - BGRA_UIntPtr");
|
||||
Console.WriteLine(" 56 - R_Int64");
|
||||
Console.WriteLine(" 57 - RG_Int64");
|
||||
Console.WriteLine(" 58 - RGB_Int64");
|
||||
Console.WriteLine(" 59 - RGBA_Int64");
|
||||
Console.WriteLine(" 60 - A_Int64");
|
||||
Console.WriteLine(" 61 - ARGB_Int64");
|
||||
Console.WriteLine(" 62 - BGRA_Int64");
|
||||
Console.WriteLine(" 63 - R_UInt64");
|
||||
Console.WriteLine(" 64 - RG_UInt64");
|
||||
Console.WriteLine(" 65 - RGB_UInt64");
|
||||
Console.WriteLine(" 66 - RGBA_UInt64");
|
||||
Console.WriteLine(" 67 - A_UInt64");
|
||||
Console.WriteLine(" 68 - ARGB_UInt64");
|
||||
Console.WriteLine(" 69 - BGRA_UInt64");
|
||||
Console.WriteLine(" 70 - R_Int128");
|
||||
Console.WriteLine(" 71 - RG_Int128");
|
||||
Console.WriteLine(" 72 - RGB_Int128");
|
||||
Console.WriteLine(" 73 - RGBA_Int128");
|
||||
Console.WriteLine(" 74 - A_Int128");
|
||||
Console.WriteLine(" 75 - ARGB_Int128");
|
||||
Console.WriteLine(" 76 - BGRA_Int128");
|
||||
Console.WriteLine(" 77 - R_UInt128");
|
||||
Console.WriteLine(" 78 - RG_UInt128");
|
||||
Console.WriteLine(" 79 - RGB_UInt128");
|
||||
Console.WriteLine(" 80 - RGBA_UInt128");
|
||||
Console.WriteLine(" 81 - A_UInt128");
|
||||
Console.WriteLine(" 82 - ARGB_UInt128");
|
||||
Console.WriteLine(" 83 - BGRA_UInt128");
|
||||
Console.WriteLine(" 84 - R_Half");
|
||||
Console.WriteLine(" 85 - RG_Half");
|
||||
Console.WriteLine(" 86 - RGB_Half");
|
||||
Console.WriteLine(" 87 - RGBA_Half");
|
||||
Console.WriteLine(" 88 - A_Half");
|
||||
Console.WriteLine(" 89 - ARGB_Half");
|
||||
Console.WriteLine(" 90 - BGRA_Half");
|
||||
Console.WriteLine(" 91 - R_Single");
|
||||
Console.WriteLine(" 92 - RG_Single");
|
||||
Console.WriteLine(" 93 - RGB_Single");
|
||||
Console.WriteLine(" 94 - RGBA_Single");
|
||||
Console.WriteLine(" 95 - A_Single");
|
||||
Console.WriteLine(" 96 - ARGB_Single");
|
||||
Console.WriteLine(" 97 - BGRA_Single");
|
||||
Console.WriteLine(" 98 - R_NFloat");
|
||||
Console.WriteLine(" 99 - RG_NFloat");
|
||||
Console.WriteLine(" 100 - RGB_NFloat");
|
||||
Console.WriteLine(" 101 - RGBA_NFloat");
|
||||
Console.WriteLine(" 102 - A_NFloat");
|
||||
Console.WriteLine(" 103 - ARGB_NFloat");
|
||||
Console.WriteLine(" 104 - BGRA_NFloat");
|
||||
Console.WriteLine(" 105 - R_Double");
|
||||
Console.WriteLine(" 106 - RG_Double");
|
||||
Console.WriteLine(" 107 - RGB_Double");
|
||||
Console.WriteLine(" 108 - RGBA_Double");
|
||||
Console.WriteLine(" 109 - A_Double");
|
||||
Console.WriteLine(" 110 - ARGB_Double");
|
||||
Console.WriteLine(" 111 - BGRA_Double");
|
||||
Console.WriteLine(" 112 - R_Decimal");
|
||||
Console.WriteLine(" 113 - RG_Decimal");
|
||||
Console.WriteLine(" 114 - RGB_Decimal");
|
||||
Console.WriteLine(" 115 - RGBA_Decimal");
|
||||
Console.WriteLine(" 116 - A_Decimal");
|
||||
Console.WriteLine(" 117 - ARGB_Decimal");
|
||||
Console.WriteLine(" 118 - BGRA_Decimal");
|
||||
Console.WriteLine(" 119 - ARGB16");
|
||||
Console.WriteLine(" 120 - RGB16");
|
||||
Console.WriteLine(" 121 - RGB9e5");
|
||||
Console.WriteLine(" 122 - RGBA16");
|
||||
int mode = int.Parse(modeString);
|
||||
switch(mode)
|
||||
{
|
||||
case 0:
|
||||
RgbConverter.Convert<ColorR<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 1:
|
||||
RgbConverter.Convert<ColorRG<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 2:
|
||||
RgbConverter.Convert<ColorRGB<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 3:
|
||||
RgbConverter.Convert<ColorRGBA<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 4:
|
||||
RgbConverter.Convert<ColorA<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 5:
|
||||
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 6:
|
||||
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<sbyte>, sbyte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 7:
|
||||
RgbConverter.Convert<ColorRGB<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 8:
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 9:
|
||||
RgbConverter.Convert<ColorA<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 10:
|
||||
RgbConverter.Convert<ColorR<short>, short, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 11:
|
||||
RgbConverter.Convert<ColorRG<short>, short, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 12:
|
||||
RgbConverter.Convert<ColorRGB<short>, short, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 13:
|
||||
RgbConverter.Convert<ColorRGBA<short>, short, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<byte>, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 14:
|
||||
RgbConverter.Convert<ColorA<short>, short, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 15:
|
||||
RgbConverter.Convert<ColorR<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 16:
|
||||
RgbConverter.Convert<ColorRG<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 17:
|
||||
RgbConverter.Convert<ColorRGB<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 18:
|
||||
RgbConverter.Convert<ColorRGBA<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 19:
|
||||
RgbConverter.Convert<ColorA<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 20:
|
||||
RgbConverter.Convert<ColorR<int>, int, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<short>, short, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 21:
|
||||
RgbConverter.Convert<ColorRG<int>, int, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 22:
|
||||
RgbConverter.Convert<ColorRGB<int>, int, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 23:
|
||||
RgbConverter.Convert<ColorRGBA<int>, int, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 24:
|
||||
RgbConverter.Convert<ColorA<int>, int, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 25:
|
||||
RgbConverter.Convert<ColorR<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 26:
|
||||
RgbConverter.Convert<ColorRG<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 27:
|
||||
RgbConverter.Convert<ColorRGB<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<ushort>, ushort, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 28:
|
||||
RgbConverter.Convert<ColorRGBA<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 29:
|
||||
RgbConverter.Convert<ColorA<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 30:
|
||||
RgbConverter.Convert<ColorR<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 31:
|
||||
RgbConverter.Convert<ColorRG<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 32:
|
||||
RgbConverter.Convert<ColorRGB<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 33:
|
||||
RgbConverter.Convert<ColorRGBA<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 34:
|
||||
RgbConverter.Convert<ColorA<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<int>, int, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 35:
|
||||
RgbConverter.Convert<ColorR<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 36:
|
||||
RgbConverter.Convert<ColorRG<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 37:
|
||||
RgbConverter.Convert<ColorRGB<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 38:
|
||||
RgbConverter.Convert<ColorRGBA<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 39:
|
||||
RgbConverter.Convert<ColorA<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 40:
|
||||
RgbConverter.Convert<ColorR<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 41:
|
||||
RgbConverter.Convert<ColorRG<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<uint>, uint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 42:
|
||||
RgbConverter.Convert<ColorRGB<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 43:
|
||||
RgbConverter.Convert<ColorRGBA<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 44:
|
||||
RgbConverter.Convert<ColorA<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 45:
|
||||
RgbConverter.Convert<ColorR<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 46:
|
||||
RgbConverter.Convert<ColorRG<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 47:
|
||||
RgbConverter.Convert<ColorRGB<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 48:
|
||||
RgbConverter.Convert<ColorRGBA<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<nint>, nint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 49:
|
||||
RgbConverter.Convert<ColorA<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 50:
|
||||
RgbConverter.Convert<ColorR<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 51:
|
||||
RgbConverter.Convert<ColorRG<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 52:
|
||||
RgbConverter.Convert<ColorRGB<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 53:
|
||||
RgbConverter.Convert<ColorRGBA<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 54:
|
||||
RgbConverter.Convert<ColorA<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 55:
|
||||
RgbConverter.Convert<ColorR<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<nuint>, nuint, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 56:
|
||||
RgbConverter.Convert<ColorRG<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 57:
|
||||
RgbConverter.Convert<ColorRGB<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 58:
|
||||
RgbConverter.Convert<ColorRGBA<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 59:
|
||||
RgbConverter.Convert<ColorA<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 60:
|
||||
RgbConverter.Convert<ColorR<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 61:
|
||||
RgbConverter.Convert<ColorRG<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 62:
|
||||
RgbConverter.Convert<ColorRGB<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<long>, long, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 63:
|
||||
RgbConverter.Convert<ColorRGBA<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 64:
|
||||
RgbConverter.Convert<ColorA<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 65:
|
||||
RgbConverter.Convert<ColorR<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 66:
|
||||
RgbConverter.Convert<ColorRG<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 67:
|
||||
RgbConverter.Convert<ColorRGB<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 68:
|
||||
RgbConverter.Convert<ColorRGBA<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 69:
|
||||
RgbConverter.Convert<ColorA<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<ulong>, ulong, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 70:
|
||||
RgbConverter.Convert<ColorR<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 71:
|
||||
RgbConverter.Convert<ColorRG<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 72:
|
||||
RgbConverter.Convert<ColorRGB<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 73:
|
||||
RgbConverter.Convert<ColorRGBA<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 74:
|
||||
RgbConverter.Convert<ColorA<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 75:
|
||||
RgbConverter.Convert<ColorR<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 76:
|
||||
RgbConverter.Convert<ColorRG<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<Int128>, Int128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 77:
|
||||
RgbConverter.Convert<ColorRGB<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 78:
|
||||
RgbConverter.Convert<ColorRGBA<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 79:
|
||||
RgbConverter.Convert<ColorA<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 80:
|
||||
RgbConverter.Convert<ColorR<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 81:
|
||||
RgbConverter.Convert<ColorRG<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 82:
|
||||
RgbConverter.Convert<ColorRGB<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 83:
|
||||
RgbConverter.Convert<ColorRGBA<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<UInt128>, UInt128, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 84:
|
||||
RgbConverter.Convert<ColorA<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 85:
|
||||
RgbConverter.Convert<ColorARGB16, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 86:
|
||||
RgbConverter.Convert<ColorARGB32, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 87:
|
||||
RgbConverter.Convert<ColorBGRA32, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 88:
|
||||
RgbConverter.Convert<ColorRGB16, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 89:
|
||||
RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorARGB<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 90:
|
||||
RgbConverter.Convert<ColorRGBA16, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorBGRA<Half>, Half, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 91:
|
||||
RgbConverter.Convert<ColorR<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 92:
|
||||
RgbConverter.Convert<ColorRG<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 93:
|
||||
RgbConverter.Convert<ColorRGB<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 94:
|
||||
RgbConverter.Convert<ColorRGBA<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 95:
|
||||
RgbConverter.Convert<ColorA<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 96:
|
||||
RgbConverter.Convert<ColorARGB<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 97:
|
||||
RgbConverter.Convert<ColorBGRA<float>, float, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 98:
|
||||
RgbConverter.Convert<ColorR<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 99:
|
||||
RgbConverter.Convert<ColorRG<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 100:
|
||||
RgbConverter.Convert<ColorRGB<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 101:
|
||||
RgbConverter.Convert<ColorRGBA<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 102:
|
||||
RgbConverter.Convert<ColorA<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 103:
|
||||
RgbConverter.Convert<ColorARGB<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 104:
|
||||
RgbConverter.Convert<ColorBGRA<NFloat>, NFloat, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 105:
|
||||
RgbConverter.Convert<ColorR<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 106:
|
||||
RgbConverter.Convert<ColorRG<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 107:
|
||||
RgbConverter.Convert<ColorRGB<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 108:
|
||||
RgbConverter.Convert<ColorRGBA<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 109:
|
||||
RgbConverter.Convert<ColorA<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 110:
|
||||
RgbConverter.Convert<ColorARGB<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 111:
|
||||
RgbConverter.Convert<ColorBGRA<double>, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 112:
|
||||
RgbConverter.Convert<ColorR<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 113:
|
||||
RgbConverter.Convert<ColorRG<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 114:
|
||||
RgbConverter.Convert<ColorRGB<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 115:
|
||||
RgbConverter.Convert<ColorRGBA<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 116:
|
||||
RgbConverter.Convert<ColorA<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 117:
|
||||
RgbConverter.Convert<ColorARGB<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 118:
|
||||
RgbConverter.Convert<ColorBGRA<decimal>, decimal, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 119:
|
||||
RgbConverter.Convert<ColorARGB16, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 120:
|
||||
RgbConverter.Convert<ColorRGB16, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 121:
|
||||
RgbConverter.Convert<ColorRGB9e5, double, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
case 122:
|
||||
RgbConverter.Convert<ColorRGBA16, byte, Color<byte, B, G, R, A>, byte>(input, width, height, output);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(mode.ToString());
|
||||
|
||||
@@ -68,11 +68,18 @@ public static class CSharpPrimitives
|
||||
return type == typeof(Half) || type == typeof(float) || type == typeof(NFloat) || type == typeof(double) || type == typeof(decimal);
|
||||
}
|
||||
|
||||
public static bool IsInteger(Type type)
|
||||
{
|
||||
return IsUnsignedInteger(type) || IsSignedInteger(type);
|
||||
}
|
||||
|
||||
public static bool IsUnsignedInteger(Type type)
|
||||
{
|
||||
return type == typeof(byte) || type == typeof(ushort) || type == typeof(uint) || type == typeof(nuint) || type == typeof(ulong) || type == typeof(UInt128);
|
||||
}
|
||||
|
||||
public static bool IsSignedInteger(Type type) => IsSignedInteger(type, out _);
|
||||
|
||||
public static bool IsSignedInteger(Type type, [NotNullWhen(true)] out Type? unsignedType)
|
||||
{
|
||||
if (type == typeof(sbyte))
|
||||
@@ -195,6 +202,21 @@ public static class CSharpPrimitives
|
||||
}
|
||||
}
|
||||
|
||||
public static Data GetDataForIntegerFloatingPointConversion(Data integerType, Data floatingPointType)
|
||||
{
|
||||
if (!IsInteger(integerType.Type))
|
||||
{
|
||||
throw new ArgumentException("The provided integer type is not an integer type.", nameof(integerType));
|
||||
}
|
||||
if (!IsFloatingPoint(floatingPointType.Type))
|
||||
{
|
||||
throw new ArgumentException("The provided floating point type is not a floating point type.", nameof(floatingPointType));
|
||||
}
|
||||
|
||||
Data result = integerType.Size < sizeof(uint) ? Dictionary[typeof(float)] : Dictionary[typeof(double)];
|
||||
return floatingPointType.Size >= result.Size ? floatingPointType : result;
|
||||
}
|
||||
|
||||
public abstract class Data
|
||||
{
|
||||
public abstract Type Type { get; }
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.1.0" />
|
||||
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,5 +1,6 @@
|
||||
using AssetRipper.Text.SourceGeneration;
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using AssetRipper.TextureDecoder.SourceGeneration.Common;
|
||||
using System.CodeDom.Compiler;
|
||||
@@ -12,8 +13,6 @@ namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
internal static readonly List<GenerationData> dataList = new()
|
||||
{
|
||||
GenerationData.Create<ColorARGB16>(2),
|
||||
GenerationData.Create<ColorARGB32>(4),
|
||||
GenerationData.Create<ColorBGRA32>(4),
|
||||
GenerationData.Create<ColorRGB16>(2),
|
||||
GenerationData.Create<ColorRGB9e5>(4),
|
||||
GenerationData.Create<ColorRGBA16>(2),
|
||||
@@ -35,7 +34,8 @@ namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
writer.WriteGeneratedCodeWarning();
|
||||
writer.WriteLine();
|
||||
writer.WriteLine($"using {typeof(RgbConverter).Namespace};");
|
||||
writer.WriteLine($"using {typeof(ColorBGRA32).Namespace};");
|
||||
writer.WriteLine($"using {typeof(B).Namespace};");
|
||||
writer.WriteLine($"using {typeof(ColorRGBA16).Namespace};");
|
||||
writer.WriteLine($"using {typeof(NFloat).Namespace};");
|
||||
writer.WriteLine();
|
||||
|
||||
@@ -62,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;");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,13 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="NUnit" Version="4.0.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.10.0">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
|
||||
<PackageReference Include="NUnit" Version="4.6.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="4.14.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using AssetRipper.TextureDecoder.Astc;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests;
|
||||
|
||||
@@ -67,7 +68,7 @@ public sealed class AstcTests
|
||||
private static void AssertCorrectDecompression<T>(int blockWidth, int blockHeight, double maxMeanDeviation, double maxStandardDeviation) where T : ITexture
|
||||
{
|
||||
ReadOnlySpan<byte> data = T.Data;
|
||||
int bytesRead = AstcDecoder.DecodeASTC(data, T.Width, T.Height, blockWidth, blockHeight, out byte[] decompressedData);
|
||||
int bytesRead = AstcDecoder.DecodeASTC<ColorBGRA<byte>, byte>(data, T.Width, T.Height, blockWidth, blockHeight, out byte[] decompressedData);
|
||||
if (!T.Mips)
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
@@ -76,8 +77,10 @@ public sealed class AstcTests
|
||||
}
|
||||
|
||||
[TestCase(new byte[16] { 83, 1, 147, 104, 198, 173, 55, 116, 182, 66, 105, 11, 102, 43, 148, 125 }, 8, 8, TestName = "GetBits Should not throw in DecodeIntseq")]
|
||||
[TestCase(new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 8, 8, TestName = "Zero blocks should not throw", Description = "Blocks of all zeros are valid")]
|
||||
[TestCase(new byte[16] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 8, 8, TestName = "0xFF blocks should not throw", Description = "Blocks with all bits set are invalid, but should be handled gracefully")]
|
||||
public void DecodesBlockWithoutThrowing(byte[] block, int blockWidth, int blockHeight)
|
||||
{
|
||||
Assert.DoesNotThrow(() => AstcDecoder.DecodeASTC(block, blockWidth, blockHeight, blockWidth, blockHeight, out _));
|
||||
Assert.DoesNotThrow(() => AstcDecoder.DecodeASTC<ColorBGRA<byte>, byte>(block, blockWidth, blockHeight, blockWidth, blockHeight, out _));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class AtcTests
|
||||
{
|
||||
@@ -6,24 +8,24 @@
|
||||
public void DecompressAtcRgb4Test()
|
||||
{
|
||||
byte[] data = File.ReadAllBytes(TestFileFolders.AtcTestFiles + "test.atc_rgb4");
|
||||
int bytesRead = Atc.AtcDecoder.DecompressAtcRgb4(data, 256, 256, out byte[] decodedData);
|
||||
Assert.Multiple(() =>
|
||||
int bytesRead = Atc.AtcDecoder.DecompressAtcRgb4<ColorBGRA<byte>, byte>(data, 256, 256, out byte[] decodedData);
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Is.EqualTo(File.ReadAllBytes(TestFileFolders.AtcTestFiles + "test.atc_rgb4_decoded")));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressAtcRgba8Test()
|
||||
{
|
||||
byte[] data = File.ReadAllBytes(TestFileFolders.AtcTestFiles + "test.atc_rgba8");
|
||||
int bytesRead = Atc.AtcDecoder.DecompressAtcRgba8(data, 256, 256, out byte[] decodedData);
|
||||
Assert.Multiple(() =>
|
||||
int bytesRead = Atc.AtcDecoder.DecompressAtcRgba8<ColorBGRA<byte>, byte>(data, 256, 256, out byte[] decodedData);
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Is.EqualTo(File.ReadAllBytes(TestFileFolders.AtcTestFiles + "test.atc_rgba8_decoded")));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,14 +21,14 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressDXT1Test()
|
||||
public void Decompress_BC1()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt1");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4 }) //mipmaps 2 and 1 cause a buffer overrun for the output
|
||||
{
|
||||
int bytesRead = Bc1.Decompress(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT1(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
int bytesRead = Bc1.Decompress<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT1<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
AssertAlmostEqual(dxtDecodedData, bcDecodedData);
|
||||
}
|
||||
@@ -36,14 +36,14 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressDXT3Test()
|
||||
public void Decompress_BC2()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt3");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4 }) //mipmaps 2 and 1 cause a buffer overrun for the output
|
||||
{
|
||||
int bytesRead = Bc2.Decompress(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT3(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
int bytesRead = Bc2.Decompress<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT3<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
AssertAlmostEqual(dxtDecodedData, bcDecodedData);
|
||||
}
|
||||
@@ -51,14 +51,14 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressDXT5Test()
|
||||
public void Decompress_BC3()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt5");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4 }) //mipmaps 2 and 1 cause a buffer overrun for the output
|
||||
{
|
||||
int bytesRead = Bc3.Decompress(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT5(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
int bytesRead = Bc3.Decompress<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT5<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
AssertAlmostEqual(dxtDecodedData, bcDecodedData);
|
||||
}
|
||||
@@ -66,25 +66,25 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC4Test()
|
||||
public void Decompress_BC4()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc4");
|
||||
int bytesRead = Bc4.Decompress(data, 512, 512, out _);
|
||||
int bytesRead = Bc4.Decompress<ColorBGRA<byte>, byte>(data, 512, 512, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC5Test()
|
||||
public void Decompress_BC5()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc5");
|
||||
int bytesRead = Bc5.Decompress(data, 512, 512, out _);
|
||||
int bytesRead = Bc5.Decompress<ColorBGRA<byte>, byte>(data, 512, 512, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[TestCase("test.bc6h_fast")]
|
||||
[TestCase("test.bc6h_normal")]
|
||||
[TestCase("test.bc6h_best")]
|
||||
public void DecompressBC6HTest(string fileName)
|
||||
public void Decompress_BC6H(string fileName)
|
||||
{
|
||||
AssertCorrectBC6HDecompression(TestFileFolders.BcTestFiles + fileName, 512, 512, false);
|
||||
}
|
||||
@@ -92,28 +92,99 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
[TestCase("test.bc7_fast")]
|
||||
[TestCase("test.bc7_normal")]
|
||||
[TestCase("test.bc7_best")]
|
||||
public void DecompressBC7Test(string fileName)
|
||||
public void Decompress_BC7(string fileName)
|
||||
{
|
||||
AssertCorrectBC7Decompression(TestFileFolders.BcTestFiles + fileName, 512, 512);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("Not implemented")]
|
||||
public void BC7PartialBlockTest([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
public void PartialBlock_BC1([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt1").AsSpan()[..Bc1.BlockSize];
|
||||
int bytesRead = Bc1.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartialBlock_BC2([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt3").AsSpan()[..Bc2.BlockSize];
|
||||
int bytesRead = Bc2.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartialBlock_BC3([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt5").AsSpan()[..Bc3.BlockSize];
|
||||
int bytesRead = Bc3.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartialBlock_BC4([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc4").AsSpan()[..Bc4.BlockSize];
|
||||
int bytesRead = Bc4.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartialBlock_BC5([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc5").AsSpan()[..Bc5.BlockSize];
|
||||
int bytesRead = Bc5.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartialBlock_BC6H([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc6h_best").AsSpan()[..Bc6h.BlockSize];
|
||||
int bytesRead = Bc6h.Decompress<ColorBGRA<byte>, byte>(data, width, height, false, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartialBlock_BC7([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc7_best").AsSpan()[..Bc7.BlockSize];
|
||||
int bytesRead = Bc7.Decompress(data, width, height, out byte[] decodedData);
|
||||
int bytesRead = Bc7.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA32>()));
|
||||
});
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertCorrectBC6HDecompression(string path, int width, int height, bool isSigned)
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(path);
|
||||
int bytesRead = Bc6h.Decompress(data, width, height, isSigned, out byte[] decodedData);
|
||||
int bytesRead = Bc6h.Decompress<ColorBGRA<byte>, byte>(data, width, height, isSigned, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
ByteArrayDeviation.AssertMinimalDeviation(decodedData, originalBgra32LogoData, MaxMeanDeviationBc6h, MaxStandardDeviationBc6h);
|
||||
}
|
||||
@@ -121,7 +192,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
private static void AssertCorrectBC7Decompression(string path, int width, int height)
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(path);
|
||||
int bytesRead = Bc7.Decompress(data, width, height, out byte[] decodedData);
|
||||
int bytesRead = Bc7.Decompress<ColorBGRA<byte>, byte>(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
ByteArrayDeviation.AssertMinimalDeviation(decodedData, originalBgra32LogoData, MaxMeanDeviationBc7, MaxStandardDeviationBc7);
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ internal static class ByteArrayDeviation
|
||||
double standardDeviation = Math.Sqrt(sumOfSquaredDeviations / (decoded.Length - 1));
|
||||
//Not sure if Bessel's correction is needed here, but it doesn't hurt, especially since length is around 1 million in the current use.
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(mean, Is.LessThan(maxMeanDeviation), "Mean too far positive");
|
||||
Assert.That(mean, Is.GreaterThan(-maxMeanDeviation), "Mean too far negative");
|
||||
Assert.That(standardDeviation, Is.LessThan(maxStandardDeviation), "Standard deviation too large");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class DxtTests
|
||||
{
|
||||
@@ -9,7 +11,7 @@
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT1(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT1<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -22,7 +24,7 @@
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT3(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT3<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -35,7 +37,7 @@
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT5(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT5<ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
|
||||
@@ -6,14 +6,14 @@ namespace AssetRipper.TextureDecoder.Tests;
|
||||
|
||||
public sealed class EtcTests
|
||||
{
|
||||
private static DecodingDelegate ETCDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressETC(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate ETC2Delegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressETC2(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate ETC2A1Delegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressETC2A1(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate ETC2A8Delegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressETC2A8(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate EACRSignedDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressEACRSigned(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate EACRUnsignedDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressEACRUnsigned(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate EACRGSignedDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressEACRGSigned(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate EACRGUnsignedDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressEACRGUnsigned(data, width, height, out decompressedData);
|
||||
private static DecodingDelegate ETCDelegate { get; } = EtcDecoder.DecompressETC<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate ETC2Delegate { get; } = EtcDecoder.DecompressETC2<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate ETC2A1Delegate { get; } = EtcDecoder.DecompressETC2A1<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate ETC2A8Delegate { get; } = EtcDecoder.DecompressETC2A8<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate EACRSignedDelegate { get; } = EtcDecoder.DecompressEACRSigned<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate EACRUnsignedDelegate { get; } = EtcDecoder.DecompressEACRUnsigned<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate EACRGSignedDelegate { get; } = EtcDecoder.DecompressEACRGSigned<ColorBGRA<byte>, byte>;
|
||||
private static DecodingDelegate EACRGUnsignedDelegate { get; } = EtcDecoder.DecompressEACRGUnsigned<ColorBGRA<byte>, byte>;
|
||||
|
||||
[Test]
|
||||
public void DecompressETCTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc", ETCDelegate);
|
||||
@@ -57,7 +57,7 @@ public sealed class EtcTests
|
||||
[Test]
|
||||
public void CorrectnessEACRGUnsignedTest() => AssertCorrectDecompression<AndroidTextures.Logo_15, ColorRG<byte>>(EACRGUnsignedDelegate, .1, .6);
|
||||
|
||||
private delegate int DecodingDelegate(ArraySegment<byte> data, int width, int height, out byte[] decompressedData);
|
||||
private delegate int DecodingDelegate(ReadOnlySpan<byte> data, int width, int height, out byte[] decompressedData);
|
||||
|
||||
private static void AssertCorrectByteCountReadFor256SquareWithMips(string path, DecodingDelegate decoder)
|
||||
{
|
||||
@@ -65,7 +65,7 @@ public sealed class EtcTests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = decoder.Invoke(new ArraySegment<byte>(data, totalBytesRead, data.Length - totalBytesRead), size, size, out _);
|
||||
int bytesRead = decoder.Invoke(new ReadOnlySpan<byte>(data, totalBytesRead, data.Length - totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -79,7 +79,7 @@ public sealed class EtcTests
|
||||
private static void AssertCorrectDecompression<TTexture, TColor>(DecodingDelegate decoder, double maxMeanDeviation, double maxStandardDeviation) where TTexture : ITexture where TColor : unmanaged, IColor<TColor, byte>
|
||||
{
|
||||
byte[] data = TTexture.Data;
|
||||
int bytesRead = decoder.Invoke(new ArraySegment<byte>(data), TTexture.Width, TTexture.Height, out byte[] decompressedData);
|
||||
int bytesRead = decoder.Invoke(new ReadOnlySpan<byte>(data), TTexture.Width, TTexture.Height, out byte[] decompressedData);
|
||||
if (!TTexture.Mips)
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
@@ -88,12 +88,12 @@ public sealed class EtcTests
|
||||
byte[] comparisonData = AndroidTextures.Logo_00.Data;
|
||||
|
||||
//Remove unused color channels
|
||||
if (typeof(TColor) != typeof(ColorBGRA32) && typeof(TColor) != typeof(ColorRGBA<byte>))
|
||||
if (typeof(TColor) != typeof(ColorBGRA<byte>) && typeof(TColor) != typeof(ColorRGBA<byte>))
|
||||
{
|
||||
Span<ColorBGRA32> pixels = MemoryMarshal.Cast<byte, ColorBGRA32>((Span<byte>)comparisonData);
|
||||
Span<ColorBGRA<byte>> pixels = MemoryMarshal.Cast<byte, ColorBGRA<byte>>((Span<byte>)comparisonData);
|
||||
for (int i = 0; i < pixels.Length; i++)
|
||||
{
|
||||
pixels[i] = pixels[i].Convert<ColorBGRA32, byte, TColor, byte>().Convert<TColor, byte, ColorBGRA32, byte>();
|
||||
pixels[i] = pixels[i].Convert<ColorBGRA<byte>, byte, TColor, byte>().Convert<TColor, byte, ColorBGRA<byte>, byte>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,12 +57,12 @@ public partial class ColorARGB16Tests
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -73,12 +73,12 @@ public partial class ColorARGB16Tests
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -89,12 +89,12 @@ public partial class ColorARGB16Tests
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -105,12 +105,12 @@ public partial class ColorARGB16Tests
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -118,13 +118,13 @@ public partial class ColorARGB16Tests
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -133,31 +133,19 @@ public partial class ColorARGB16Tests
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public static ColorARGB16 MakeRandomColor() => ColorRandom<ColorARGB16, byte>.MakeRandomColor();
|
||||
|
||||
public static byte MakeRandomValue() => ColorRandom<ColorARGB16, byte>.MakeRandomValue();
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorARGB32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB16, byte, ColorARGB32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorBGRA32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB16, byte, ColorBGRA32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_sbyte()
|
||||
{
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorARGB32Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorARGB32>(), Is.EqualTo(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorARGB32 MakeRandomColor() => ColorRandom<ColorARGB32, byte>.MakeRandomColor();
|
||||
|
||||
public static byte MakeRandomValue() => ColorRandom<ColorARGB32, byte>.MakeRandomValue();
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorARGB32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorARGB32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorBGRA32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorBGRA32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_sbyte()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<sbyte>, sbyte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_byte()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<byte>, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_short()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<short>, short>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_ushort()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<ushort>, ushort>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_int()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<int>, int>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_uint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<uint>, uint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<nint>, nint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nuint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<nuint>, nuint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_long()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<long>, long>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_ulong()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<ulong>, ulong>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_Int128()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<Int128>, Int128>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_UInt128()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<UInt128>, UInt128>();
|
||||
}
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorBGRA32Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorBGRA32>(), Is.EqualTo(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorBGRA32 MakeRandomColor() => ColorRandom<ColorBGRA32, byte>.MakeRandomColor();
|
||||
|
||||
public static byte MakeRandomValue() => ColorRandom<ColorBGRA32, byte>.MakeRandomValue();
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorARGB32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorARGB32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorBGRA32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorBGRA32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_sbyte()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<sbyte>, sbyte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_byte()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<byte>, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_short()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<short>, short>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_ushort()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<ushort>, ushort>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_int()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<int>, int>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_uint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<uint>, uint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<nint>, nint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nuint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<nuint>, nuint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_long()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<long>, long>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_ulong()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<ulong>, ulong>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_Int128()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<Int128>, Int128>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_UInt128()
|
||||
{
|
||||
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<UInt128>, UInt128>();
|
||||
}
|
||||
}
|
||||
@@ -57,12 +57,12 @@ public partial class ColorRGB16Tests
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -73,12 +73,12 @@ public partial class ColorRGB16Tests
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -89,12 +89,12 @@ public partial class ColorRGB16Tests
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -105,12 +105,12 @@ public partial class ColorRGB16Tests
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -118,13 +118,13 @@ public partial class ColorRGB16Tests
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -133,31 +133,19 @@ public partial class ColorRGB16Tests
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public static ColorRGB16 MakeRandomColor() => ColorRandom<ColorRGB16, byte>.MakeRandomColor();
|
||||
|
||||
public static byte MakeRandomValue() => ColorRandom<ColorRGB16, byte>.MakeRandomValue();
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorARGB32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorARGB32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorBGRA32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorBGRA32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_sbyte()
|
||||
{
|
||||
|
||||
@@ -18,13 +18,13 @@ public partial class ColorRGB9e5Tests
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -33,13 +33,13 @@ public partial class ColorRGB9e5Tests
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public static ColorRGB9e5 MakeRandomColor() => ColorRandom<ColorRGB9e5, double>.MakeRandomColor();
|
||||
|
||||
@@ -57,12 +57,12 @@ public partial class ColorRGBA16Tests
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -73,12 +73,12 @@ public partial class ColorRGBA16Tests
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -89,12 +89,12 @@ public partial class ColorRGBA16Tests
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -105,12 +105,12 @@ public partial class ColorRGBA16Tests
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -118,13 +118,13 @@ public partial class ColorRGBA16Tests
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -133,31 +133,19 @@ public partial class ColorRGBA16Tests
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public static ColorRGBA16 MakeRandomColor() => ColorRandom<ColorRGBA16, byte>.MakeRandomColor();
|
||||
|
||||
public static byte MakeRandomValue() => ColorRandom<ColorRGBA16, byte>.MakeRandomValue();
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorARGB32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGBA16, byte, ColorARGB32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorBGRA32()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGBA16, byte, ColorBGRA32, byte>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_sbyte()
|
||||
{
|
||||
|
||||
@@ -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,86 +8,120 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<sbyte>), typeof(sbyte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<sbyte>), typeof(sbyte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<sbyte>), typeof(sbyte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<sbyte>), typeof(sbyte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<sbyte>), typeof(sbyte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<byte>), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<short>), typeof(short) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<ushort>), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<int>), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<uint>), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<nint>), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<nuint>), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<ulong>), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<Int128>), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<UInt128>), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<Half>), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<float>), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<NFloat>), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorARGB<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorBGRA<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<decimal>), typeof(decimal) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<decimal>), typeof(decimal) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<decimal>), typeof(decimal) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<decimal>), typeof(decimal) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<decimal>), typeof(decimal) })]
|
||||
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>
|
||||
{
|
||||
}
|
||||
+20
-20
@@ -9,26 +9,26 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = [typeof(long), typeof(ulong)])]
|
||||
[TestFixture(TypeArgs = [typeof(Int128), typeof(UInt128)])]
|
||||
internal class NumericConversionExtremesTests<TSigned, TUnsigned>
|
||||
where TSigned : unmanaged, IMinMaxValue<TSigned>
|
||||
where TUnsigned : unmanaged, IMinMaxValue<TUnsigned>
|
||||
where TSigned : unmanaged, IMinMaxValue<TSigned>
|
||||
where TUnsigned : unmanaged, IMinMaxValue<TUnsigned>
|
||||
{
|
||||
[Test]
|
||||
public void SignedToUnsignedExtremesAreCorrect()
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(NumericConversion.Convert<TSigned, TUnsigned>(TSigned.MinValue), Is.EqualTo(TUnsigned.MinValue));
|
||||
Assert.That(NumericConversion.Convert<TSigned, TUnsigned>(TSigned.MaxValue), Is.EqualTo(TUnsigned.MaxValue));
|
||||
});
|
||||
}
|
||||
[Test]
|
||||
public void SignedToUnsignedExtremesAreCorrect()
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(NumericConversion.Convert<TSigned, TUnsigned>(TSigned.MinValue), Is.EqualTo(TUnsigned.MinValue));
|
||||
Assert.That(NumericConversion.Convert<TSigned, TUnsigned>(TSigned.MaxValue), Is.EqualTo(TUnsigned.MaxValue));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnsignedToSignedExtremesAreCorrect()
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(NumericConversion.Convert<TUnsigned, TSigned>(TUnsigned.MinValue), Is.EqualTo(TSigned.MinValue));
|
||||
Assert.That(NumericConversion.Convert<TUnsigned, TSigned>(TUnsigned.MaxValue), Is.EqualTo(TSigned.MaxValue));
|
||||
});
|
||||
}
|
||||
[Test]
|
||||
public void UnsignedToSignedExtremesAreCorrect()
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(NumericConversion.Convert<TUnsigned, TSigned>(TUnsigned.MinValue), Is.EqualTo(TSigned.MinValue));
|
||||
Assert.That(NumericConversion.Convert<TUnsigned, TSigned>(TUnsigned.MaxValue), Is.EqualTo(TSigned.MaxValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,4 +1,5 @@
|
||||
using AssetRipper.TextureDecoder.Pvrtc;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests;
|
||||
|
||||
@@ -11,7 +12,7 @@ public sealed class PvrtcTests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = PvrtcDecoder.DecompressPVRTC(data, size, size, false, out _);
|
||||
int bytesRead = PvrtcDecoder.DecompressPVRTC<ColorBGRA<byte>, byte>(data, size, size, false, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -32,7 +33,7 @@ public sealed class PvrtcTests
|
||||
private static void AssertCorrectDecompression<T>(bool do2bitMode, double maxMeanDeviation, double maxStandardDeviation) where T : ITexture
|
||||
{
|
||||
ReadOnlySpan<byte> data = T.Data;
|
||||
int bytesRead = PvrtcDecoder.DecompressPVRTC(data, T.Width, T.Height, do2bitMode, out byte[] decompressedData);
|
||||
int bytesRead = PvrtcDecoder.DecompressPVRTC<ColorBGRA<byte>, byte>(data, T.Width, T.Height, do2bitMode, out byte[] decompressedData);
|
||||
if (!T.Mips)
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
|
||||
+38
-38
@@ -3,7 +3,7 @@ using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class NewRgbTests
|
||||
public sealed class RgbConsistencyTests
|
||||
{
|
||||
[Test]
|
||||
public void ConvertA8Test()
|
||||
@@ -12,8 +12,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.A8ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorA<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
int bytesRead = OldRgbConverter.A8ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorA<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.Convert<ColorARGB16, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = RgbConverter.Convert<ColorARGB16, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -40,8 +40,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.RGB24ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
int bytesRead = OldRgbConverter.RGB24ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -55,8 +55,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.RGBA32ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
int bytesRead = OldRgbConverter.RGBA32ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -70,8 +70,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.ARGB32ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorARGB32, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
int bytesRead = OldRgbConverter.ARGB32ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorARGB<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -85,8 +85,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.RGB16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB16, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
int bytesRead = OldRgbConverter.RGB16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB16, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -100,7 +100,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.Convert<ColorR<ushort>, ushort, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = RgbConverter.Convert<ColorR<ushort>, ushort, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -113,7 +113,7 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.Convert<ColorRGBA16, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = RgbConverter.Convert<ColorRGBA16, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -126,8 +126,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.RG16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
int bytesRead = OldRgbConverter.RG16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -141,8 +141,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.R8ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
int bytesRead = OldRgbConverter.R8ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA<byte>, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
@@ -153,8 +153,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rhalf");
|
||||
RgbConverter.RHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorR<Half>, Half, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorR<Half>, Half, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -162,8 +162,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rghalf");
|
||||
RgbConverter.RGHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<Half>, Half, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<Half>, Half, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -171,8 +171,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGBAHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbahalf");
|
||||
RgbConverter.RGBAHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<Half>, Half, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGBAHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<Half>, Half, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -180,8 +180,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rfloat");
|
||||
RgbConverter.RFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorR<float>, float, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorR<float>, float, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -189,8 +189,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgfloat");
|
||||
RgbConverter.RGFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<float>, float, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<float>, float, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -198,8 +198,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGBAFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbafloat");
|
||||
RgbConverter.RGBAFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<float>, float, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGBAFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<float>, float, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -207,8 +207,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGB9e5FloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb9e5float");
|
||||
RgbConverter.RGB9e5FloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGB9e5FloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA<byte>, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -216,8 +216,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRG32Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rg32");
|
||||
RgbConverter.RG32ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<ushort>, ushort, ColorBGRA32, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
OldRgbConverter.RG32ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG<ushort>, ushort, ColorBGRA<byte>, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -225,8 +225,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGB48Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb48");
|
||||
RgbConverter.RGB48ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB<ushort>, ushort, ColorBGRA32, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGB48ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB<ushort>, ushort, ColorBGRA<byte>, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
@@ -234,8 +234,8 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void ConvertRGBA64Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgba64");
|
||||
RgbConverter.RGBA64ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<ushort>, ushort, ColorBGRA32, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
OldRgbConverter.RGBA64ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA<ushort>, ushort, ColorBGRA<byte>, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class RgbTests
|
||||
{
|
||||
@@ -9,7 +12,7 @@
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.A8ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = ConvertSquare<ColorA<byte>, byte>(data.Slice(totalBytesRead), size);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -22,7 +25,7 @@
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.RGB24ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = ConvertSquare<ColorRGB<byte>, byte>(data.Slice(totalBytesRead), size);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -35,7 +38,7 @@
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.RGBA32ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = ConvertSquare<ColorRGBA<byte>, byte>(data.Slice(totalBytesRead), size);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -48,7 +51,7 @@
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.ARGB32ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = ConvertSquare<ColorARGB<byte>, byte>(data.Slice(totalBytesRead), size);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -61,7 +64,7 @@
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.RGB16ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = ConvertSquare<ColorRGB16, byte>(data.Slice(totalBytesRead), size);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -74,7 +77,7 @@
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.RG16ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = ConvertSquare<ColorRG<byte>, byte>(data.Slice(totalBytesRead), size);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -87,7 +90,7 @@
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.R8ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
int bytesRead = ConvertSquare<ColorR<byte>, byte>(data.Slice(totalBytesRead), size);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
@@ -97,7 +100,7 @@
|
||||
public void ConvertRHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rhalf");
|
||||
int bytesRead = Rgb.RgbConverter.RHalfToBGRA32(data, 256, 256, out _);
|
||||
int bytesRead = ConvertSquare<ColorR<Half>, Half>(data, 256);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
@@ -105,7 +108,7 @@
|
||||
public void ConvertRGHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rghalf");
|
||||
int bytesRead = Rgb.RgbConverter.RGHalfToBGRA32(data, 256, 256, out _);
|
||||
int bytesRead = ConvertSquare<ColorRG<Half>, Half>(data, 256);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
@@ -113,7 +116,7 @@
|
||||
public void ConvertRGBAHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbahalf");
|
||||
int bytesRead = Rgb.RgbConverter.RGBAHalfToBGRA32(data, 256, 256, out _);
|
||||
int bytesRead = ConvertSquare<ColorRGBA<Half>, Half>(data, 256);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
@@ -121,7 +124,7 @@
|
||||
public void ConvertRFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rfloat");
|
||||
int bytesRead = Rgb.RgbConverter.RFloatToBGRA32(data, 256, 256, out _);
|
||||
int bytesRead = ConvertSquare<ColorR<float>, float>(data, 256);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
@@ -129,7 +132,7 @@
|
||||
public void ConvertRGFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgfloat");
|
||||
int bytesRead = Rgb.RgbConverter.RGFloatToBGRA32(data, 256, 256, out _);
|
||||
int bytesRead = ConvertSquare<ColorRG<float>, float>(data, 256);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
@@ -137,7 +140,7 @@
|
||||
public void ConvertRGBAFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbafloat");
|
||||
int bytesRead = Rgb.RgbConverter.RGBAFloatToBGRA32(data, 256, 256, out _);
|
||||
int bytesRead = ConvertSquare<ColorRGBA<float>, float>(data, 256);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
@@ -145,7 +148,7 @@
|
||||
public void ConvertRGB9e5FloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb9e5float");
|
||||
int bytesRead = Rgb.RgbConverter.RGB9e5FloatToBGRA32(data, 256, 256, out _);
|
||||
int bytesRead = ConvertSquare<ColorRGB9e5, double>(data, 256);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
@@ -153,7 +156,7 @@
|
||||
public void ConvertRG32Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rg32");
|
||||
int bytesRead = Rgb.RgbConverter.RG32ToBGRA32(data, 512, 512, out _);
|
||||
int bytesRead = ConvertSquare<ColorRG<ushort>, ushort>(data, 512);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
@@ -161,7 +164,7 @@
|
||||
public void ConvertRGB48Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb48");
|
||||
int bytesRead = Rgb.RgbConverter.RGB48ToBGRA32(data, 512, 512, out _);
|
||||
int bytesRead = ConvertSquare<ColorRGB<ushort>, ushort>(data, 512);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
@@ -169,8 +172,15 @@
|
||||
public void ConvertRGBA64Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgba64");
|
||||
int bytesRead = Rgb.RgbConverter.RGBA64ToBGRA32(data, 512, 512, out _);
|
||||
int bytesRead = ConvertSquare<ColorRGBA<ushort>, ushort>(data, 512);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
private static int ConvertSquare<TColor, TChannel>(ReadOnlySpan<byte> data, int size)
|
||||
where TColor : unmanaged, IColor<TChannel>
|
||||
where TChannel : unmanaged
|
||||
{
|
||||
return RgbConverter.Convert<TColor, TChannel, ColorBGRA<byte>, byte>(data, size, size, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,12 +13,12 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void DecompressYUY2Test()
|
||||
{
|
||||
byte[] input = File.ReadAllBytes(TestFileFolders.Yuy2TestFiles + "test.yuy2");
|
||||
int bytesRead = Yuy2Decoder.DecompressYUY2<ColorBGRA32, byte>(input, TestImageWidth, TestImageHeight, out byte[] decodedData);
|
||||
Assert.Multiple(() =>
|
||||
int bytesRead = Yuy2Decoder.DecompressYUY2<ColorBGRA<byte>, byte>(input, TestImageWidth, TestImageHeight, out byte[] decodedData);
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(input.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(TestImageWidth * TestImageHeight * Unsafe.SizeOf<ColorBGRA32>()));
|
||||
});
|
||||
Assert.That(decodedData, Has.Length.EqualTo(TestImageWidth * TestImageHeight * Unsafe.SizeOf<ColorBGRA<byte>>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<Solution>
|
||||
<Folder Name="/Solution Items/">
|
||||
<File Path=".editorconfig" />
|
||||
<File Path=".gitignore" />
|
||||
<File Path="Directory.Build.props" />
|
||||
<File Path="LICENSE" />
|
||||
<File Path="README.md" />
|
||||
</Folder>
|
||||
<Folder Name="/Solution Items/Licenses/">
|
||||
<File Path="Licenses/bcdec.md" />
|
||||
<File Path="Licenses/uTinyRipper.md" />
|
||||
</Folder>
|
||||
<Folder Name="/Solution Items/Workflows/">
|
||||
<File Path=".github/workflows/build.yml" />
|
||||
</Folder>
|
||||
<Project Path="AssetRipper.TextureDecoder.ColorGenerator/AssetRipper.TextureDecoder.ColorGenerator.csproj" />
|
||||
<Project Path="AssetRipper.TextureDecoder.ConsoleApp/AssetRipper.TextureDecoder.ConsoleApp.csproj" />
|
||||
<Project Path="AssetRipper.TextureDecoder.Paths/AssetRipper.TextureDecoder.Paths.csproj" />
|
||||
<Project Path="AssetRipper.TextureDecoder.SourceGeneration.Common/AssetRipper.TextureDecoder.SourceGeneration.Common.csproj" />
|
||||
<Project Path="AssetRipper.TextureDecoder.TestGenerator/AssetRipper.TextureDecoder.TestGenerator.csproj" />
|
||||
<Project Path="AssetRipper.TextureDecoder.Tests/AssetRipper.TextureDecoder.Tests.csproj" />
|
||||
<Project Path="AssetRipper.TextureDecoder/AssetRipper.TextureDecoder.csproj" />
|
||||
<Project Path="IntegerConversion.ConstantEstimation/IntegerConversion.ConstantEstimation.csproj" />
|
||||
</Solution>
|
||||
@@ -1,29 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
<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 - 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="8.0.0">
|
||||
<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>
|
||||
<NoWarn>1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.400">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,3 +1,5 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Astc
|
||||
@@ -12,55 +14,59 @@ 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);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecodeASTC<TOutputColor, TOutputChannelValue>(input, width, height, blockWidth, blockHeight, output);
|
||||
}
|
||||
|
||||
public static int DecodeASTC(ReadOnlySpan<byte> input, int width, int height, int blockWidth, int blockHeight, Span<byte> output)
|
||||
public static int DecodeASTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, int blockWidth, int blockHeight, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecodeASTC<TOutputColor, TOutputChannelValue>(input, width, height, blockWidth, blockHeight, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecodeASTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, int blockWidth, int blockHeight, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
ValidateBlockDimension(blockWidth);
|
||||
ValidateBlockDimension(blockHeight);
|
||||
int bcw = (width + blockWidth - 1) / blockWidth;
|
||||
int bch = (height + blockHeight - 1) / blockHeight;
|
||||
int clen_last = (width + blockWidth - 1) % blockWidth + 1;
|
||||
Span<uint> buf = stackalloc uint[blockWidth * blockHeight];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[blockWidth * blockHeight];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeBlock(input[inputOffset..], blockWidth, blockHeight, buf);
|
||||
DecodeBlock(input[inputOffset..], blockWidth, blockHeight, buffer);
|
||||
int clen = s < bcw - 1 ? blockWidth : clen_last;
|
||||
for (int i = 0, y = t * blockHeight; i < blockHeight && y < height; i++, y++)
|
||||
{
|
||||
int outputOffsetUInt32 = t * blockHeight * width + s * blockWidth + i * width;
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
WriteUInt32(output, outputOffsetUInt32 + j, buf[j + i * blockWidth]);
|
||||
output[outputOffsetUInt32 + j] = buffer[j + i * blockWidth].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
static void WriteUInt32(Span<byte> buffer, int offsetUInt32, uint value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offsetUInt32 * sizeof(uint)), value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeBlock(ReadOnlySpan<byte> input, int blockWidth, int blockHeight, Span<uint> output)
|
||||
private static void DecodeBlock(ReadOnlySpan<byte> input, int blockWidth, int blockHeight, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
if (input[0] == 0xfc && (input[1] & 1) == 1)
|
||||
{
|
||||
uint c = Color(input[9], input[11], input[13], input[15]);
|
||||
for (int i = 0; i < blockWidth * blockHeight; i++)
|
||||
{
|
||||
output[i] = c;
|
||||
}
|
||||
ColorRGBA<byte> c = new(input[9], input[11], input[13], input[15]);
|
||||
output[..(blockWidth * blockHeight)].Fill(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -674,7 +680,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplicateColor(BlockData block, Span<uint> output)
|
||||
private static void ApplicateColor(BlockData block, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
if (block.dual_plane != 0)
|
||||
{
|
||||
@@ -689,7 +695,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
byte g = SelectColor(block.endpoints[p][1], block.endpoints[p][5], block.weights[i * 2 + ps[1]]);
|
||||
byte b = SelectColor(block.endpoints[p][2], block.endpoints[p][6], block.weights[i * 2 + ps[2]]);
|
||||
byte a = SelectColor(block.endpoints[p][3], block.endpoints[p][7], block.weights[i * 2 + ps[3]]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
output[i] = new(r, g, b, a);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -700,7 +706,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
byte g = SelectColor(block.endpoints[0][1], block.endpoints[0][5], block.weights[i * 2 + ps[1]]);
|
||||
byte b = SelectColor(block.endpoints[0][2], block.endpoints[0][6], block.weights[i * 2 + ps[2]]);
|
||||
byte a = SelectColor(block.endpoints[0][3], block.endpoints[0][7], block.weights[i * 2 + ps[3]]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
output[i] = new(r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -713,7 +719,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
byte g = SelectColor(block.endpoints[p][1], block.endpoints[p][5], block.weights[i * 2]);
|
||||
byte b = SelectColor(block.endpoints[p][2], block.endpoints[p][6], block.weights[i * 2]);
|
||||
byte a = SelectColor(block.endpoints[p][3], block.endpoints[p][7], block.weights[i * 2]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
output[i] = new(r, g, b, a);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -724,7 +730,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
byte g = SelectColor(block.endpoints[0][1], block.endpoints[0][5], block.weights[i * 2]);
|
||||
byte b = SelectColor(block.endpoints[0][2], block.endpoints[0][6], block.weights[i * 2]);
|
||||
byte a = SelectColor(block.endpoints[0][3], block.endpoints[0][7], block.weights[i * 2]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
output[i] = new(r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -849,12 +855,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)
|
||||
{
|
||||
@@ -883,7 +883,12 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static int GetBits(ReadOnlySpan<byte> input, int bit, int len)
|
||||
{
|
||||
ReadOnlySpan<byte> slice = input[(bit / 8)..];
|
||||
if (bit < 0 || input.Length * 8 < bit + len)
|
||||
{
|
||||
// Invalid input, but we should return something instead of throwing an exception
|
||||
return default;
|
||||
}
|
||||
ReadOnlySpan<byte> slice = input[(bit / 8)..];
|
||||
int bitBuffer;
|
||||
if (slice.Length >= sizeof(int))
|
||||
{
|
||||
@@ -894,7 +899,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
Span<byte> temp = stackalloc byte[sizeof(int)];
|
||||
slice.CopyTo(temp);
|
||||
bitBuffer = BinaryPrimitives.ReadInt32LittleEndian(temp);
|
||||
}
|
||||
}
|
||||
return (bitBuffer >> (bit % 8)) & ((1 << len) - 1);
|
||||
}
|
||||
|
||||
@@ -996,10 +1001,12 @@ 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 =
|
||||
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,
|
||||
@@ -1019,15 +1026,15 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
|
||||
];
|
||||
|
||||
private static readonly int[] WeightPrecTableA = [0, 0, 0, 3, 0, 5, 3, 0, 0, 0, 5, 3, 0, 5, 3, 0];
|
||||
private static readonly int[] WeightPrecTableB = [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 = [0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 0, 0];
|
||||
private static readonly int[] CemTableB = [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 = [0, 2, 4, 5, 7];
|
||||
private static readonly int[] DImq = [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,
|
||||
@@ -1035,14 +1042,14 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
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 = [0, 204, 93, 44, 22, 11, 5];
|
||||
private static readonly int[] DEQuintsTable = [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,34 +1,48 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Atc
|
||||
{
|
||||
public static class AtcDecoder
|
||||
{
|
||||
public static int DecompressAtcRgb4(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressAtcRgb4<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressAtcRgb4(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressAtcRgb4<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressAtcRgb4(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressAtcRgb4<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressAtcRgb4<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressAtcRgb4<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<TOutputColor> buf = stackalloc TOutputColor[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeAtcRgb4Block(input.Slice(inputOffset, 8), buf);
|
||||
DecodeAtcRgb4Block<TOutputColor, TOutputChannelValue>(input.Slice(inputOffset, 8), buf);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
int outputOffset = t * 16 * width + s * 16 + i * width * sizeof(uint);
|
||||
int outputOffset = t * 4 * width + s * 4 + i * width;
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(output.Slice(outputOffset + j * sizeof(uint)), buf[j + 4 * i]);
|
||||
output[outputOffset + j] = buf[j + 4 * i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,31 +50,44 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressAtcRgba8(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressAtcRgba8<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressAtcRgba8(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressAtcRgba8<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressAtcRgba8(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressAtcRgba8<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressAtcRgba8<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressAtcRgba8<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<TOutputColor> buf = stackalloc TOutputColor[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeAtcRgba8Block(input.Slice(inputOffset, 16), buf);
|
||||
DecodeAtcRgba8Block<TOutputColor, TOutputChannelValue>(input.Slice(inputOffset, 16), buf);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
int outputOffset = t * 16 * width + s * 16 + i * width * sizeof(uint);
|
||||
int outputOffset = t * 4 * width + s * 4 + i * width;
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(output.Slice(outputOffset + j * sizeof(uint)), buf[j + 4 * i]);
|
||||
output[outputOffset + j] = buf[j + 4 * i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,7 +95,9 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
private static void DecodeAtcRgb4Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeAtcRgb4Block<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
Span<int> colors = stackalloc int[16];
|
||||
int c0 = input.ReadAtOffset<ushort>(0);
|
||||
@@ -83,12 +112,14 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
int cb = colors[cidx * 4 + 0];
|
||||
int cg = colors[cidx * 4 + 1];
|
||||
int cr = colors[cidx * 4 + 2];
|
||||
output[i] = Color(cr, cg, cb, 255);
|
||||
output[i].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>((byte)cr, (byte)cg, (byte)cb, byte.MaxValue);
|
||||
cindex >>= 2;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeAtcRgba8Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeAtcRgba8Block<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
Span<int> alphas = stackalloc int[16];
|
||||
ulong avalue = BinaryPrimitives.ReadUInt64LittleEndian(input);
|
||||
@@ -112,7 +143,7 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
int cr = colors[cidx * 4 + 2];
|
||||
int aidx = unchecked((int)aindex & 7);
|
||||
int ca = alphas[aidx];
|
||||
output[i] = Color(cr, cg, cb, ca);
|
||||
output[i].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>((byte)cr, (byte)cg, (byte)cb, (byte)ca);
|
||||
cindex >>= 2;
|
||||
aindex >>= 3;
|
||||
}
|
||||
@@ -121,6 +152,7 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static T ReadAtOffset<T>(this ReadOnlySpan<byte> input, int offset) where T : unmanaged
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return MemoryMarshal.Read<T>(input.Slice(offset));
|
||||
}
|
||||
|
||||
@@ -159,9 +191,9 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
colors[13] = Extend((c1 >> 5) & 0x3F, 6, 8);
|
||||
colors[14] = Extend((c1 >> 11) & 0x1F, 5, 8);
|
||||
|
||||
colors[4] = Math.Max(0, colors[8] - colors[12] / 4);
|
||||
colors[5] = Math.Max(0, colors[9] - colors[13] / 4);
|
||||
colors[6] = Math.Max(0, colors[10] - colors[14] / 4);
|
||||
colors[4] = int.Max(0, colors[8] - colors[12] / 4);
|
||||
colors[5] = int.Max(0, colors[9] - colors[13] / 4);
|
||||
colors[6] = int.Max(0, colors[10] - colors[14] / 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,11 +228,5 @@ namespace AssetRipper.TextureDecoder.Atc
|
||||
// bit-pattern replicating scaling (can at most double the bits)
|
||||
return (value << (to - from)) | (value >> (from * 2 - to));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static uint Color(int r, int g, int b, int a)
|
||||
{
|
||||
return unchecked((uint)(r << 16 | g << 8 | b | a << 24));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,26 +9,56 @@ public static class Bc1
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 8;
|
||||
/// <summary>
|
||||
/// The width of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockWidth = 4;
|
||||
/// <summary>
|
||||
/// The height of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockHeight = 4;
|
||||
/// <summary>
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
BcHelpers.DecompressBc1(input.Slice(inputOffset, BlockSize), output.Slice(outputOffset), width * 4);
|
||||
BcHelpers.DecompressBc1(input.Slice(inputOffset, BlockSize), buffer);
|
||||
BcHelpers.CopyBufferToOutput<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorRGBA<byte>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(output, width, height, output);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,26 +9,56 @@ public static class Bc2
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The width of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockWidth = 4;
|
||||
/// <summary>
|
||||
/// The height of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockHeight = 4;
|
||||
/// <summary>
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
BcHelpers.DecompressBc2(input.Slice(inputOffset), output.Slice(outputOffset), width * 4);
|
||||
BcHelpers.DecompressBc2(input.Slice(inputOffset, BlockSize), buffer);
|
||||
BcHelpers.CopyBufferToOutput<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorRGBA<byte>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(output, width, height, output);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,26 +9,56 @@ public static class Bc3
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The width of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockWidth = 4;
|
||||
/// <summary>
|
||||
/// The height of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockHeight = 4;
|
||||
/// <summary>
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
BcHelpers.DecompressBc3(input.Slice(inputOffset), output.Slice(outputOffset), width * 4);
|
||||
BcHelpers.DecompressBc3(input.Slice(inputOffset, BlockSize), buffer);
|
||||
BcHelpers.CopyBufferToOutput<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorRGBA<byte>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(output, width, height, output);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,30 +10,56 @@ public static class Bc4
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 8;
|
||||
/// <summary>
|
||||
/// The width of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockWidth = 4;
|
||||
/// <summary>
|
||||
/// The height of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockHeight = 4;
|
||||
/// <summary>
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorR<byte>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
int bufferSize = width * height * Unsafe.SizeOf<ColorR<byte>>();
|
||||
byte[] bufferArray = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
Span<byte> buffer = new Span<byte>(bufferArray, 0, bufferSize);
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int bufferOffset = ((i * width) + j) * Unsafe.SizeOf<ColorR<byte>>();
|
||||
BcHelpers.DecompressBc4(input.Slice(inputOffset), buffer.Slice(bufferOffset), width);
|
||||
BcHelpers.DecompressBc4(input.Slice(inputOffset, BlockSize), buffer);
|
||||
BcHelpers.CopyBufferToOutput<ColorR<byte>, byte, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorR<byte>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA32, byte>(buffer, width, height, output);
|
||||
ArrayPool<byte>.Shared.Return(bufferArray);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,30 +10,56 @@ public static class Bc5
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The width of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockWidth = 4;
|
||||
/// <summary>
|
||||
/// The height of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockHeight = 4;
|
||||
/// <summary>
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorRG<byte>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
int bufferSize = width * height * Unsafe.SizeOf<ColorRG<byte>>();
|
||||
byte[] bufferArray = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
Span<byte> buffer = new Span<byte>(bufferArray, 0, bufferSize);
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int bufferOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRG<byte>>();
|
||||
BcHelpers.DecompressBc5(input.Slice(inputOffset), buffer.Slice(bufferOffset), width * 2);
|
||||
BcHelpers.DecompressBc5(input.Slice(inputOffset, BlockSize), buffer);
|
||||
BcHelpers.CopyBufferToOutput<ColorRG<byte>, byte, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorRG<byte>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA32, byte>(buffer, width, height, output);
|
||||
ArrayPool<byte>.Shared.Return(bufferArray);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,34 +10,56 @@ public static class Bc6h
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The width of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockWidth = 4;
|
||||
/// <summary>
|
||||
/// The height of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockHeight = 4;
|
||||
/// <summary>
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorRGB<Half>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, bool isSigned, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, bool isSigned, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, isSigned, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, isSigned, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, bool isSigned, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, bool isSigned, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
int bufferSize = width * height * Unsafe.SizeOf<ColorRGB<Half>>();
|
||||
byte[] bufferArray = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
Span<byte> buffer = new Span<byte>(bufferArray, 0, bufferSize);
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, isSigned, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, bool isSigned, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGB<Half>>();
|
||||
BcHelpers.DecompressBc6h(
|
||||
input.Slice(inputOffset, BlockSize),
|
||||
buffer.Slice(outputOffset),
|
||||
width * 3,
|
||||
isSigned);
|
||||
BcHelpers.DecompressBc6h(input.Slice(inputOffset, BlockSize), buffer, isSigned);
|
||||
BcHelpers.CopyBufferToOutput<ColorRGB<Half>, Half, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorRGB<Half>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGB<Half>, Half, ColorBGRA32, byte>(buffer, width, height, output);
|
||||
ArrayPool<byte>.Shared.Return(bufferArray);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ internal static class Bc6hTables
|
||||
]
|
||||
];
|
||||
|
||||
internal static int[] AWeight3 { get; } = [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; } = [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];
|
||||
}
|
||||
@@ -21,29 +21,44 @@ public static class Bc7
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
/// <summary>
|
||||
/// The size of a decoded block, in bytes.
|
||||
/// </summary>
|
||||
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return Decompress(input, width, height, output);
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return Decompress<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int Decompress<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += BlockHeight)
|
||||
{
|
||||
for (int j = 0; j < width; j += BlockWidth)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * PixelSize;
|
||||
BcHelpers.DecompressBc7(
|
||||
input.Slice(inputOffset, BlockSize),
|
||||
output.Slice(outputOffset),
|
||||
width * 4);
|
||||
BcHelpers.DecompressBc7(input.Slice(inputOffset, BlockSize), buffer);
|
||||
BcHelpers.CopyBufferToOutput<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>(MemoryMarshal.Cast<byte, ColorRGBA<byte>>(buffer), output, width, height, j, i, BlockWidth, BlockHeight);
|
||||
inputOffset += BlockSize;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(output, width, height, output);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
|
||||
@@ -789,11 +789,11 @@ internal static class Bc7Tables
|
||||
]
|
||||
];
|
||||
|
||||
internal static int[] AWeight2 { get; } = [0, 21, 43, 64];
|
||||
internal static ReadOnlySpan<int> AWeight2 => [0, 21, 43, 64];
|
||||
|
||||
internal static int[] AWeight3 { get; } = [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; } = [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];
|
||||
|
||||
internal const byte bcdec_bc7_sModeHasPBits = 0b11001011;
|
||||
}
|
||||
@@ -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,61 +559,54 @@ 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)))
|
||||
};
|
||||
Span2D<int> endpoints = new(stackalloc int[6 * 4], 6, 4);
|
||||
Span2D<int> indices = new(stackalloc int[4 * 4], 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)
|
||||
@@ -646,8 +641,8 @@ internal static class BcHelpers
|
||||
// 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)
|
||||
@@ -663,7 +658,7 @@ 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;
|
||||
@@ -705,10 +700,25 @@ internal static class BcHelpers
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,10 +752,10 @@ internal static class BcHelpers
|
||||
|
||||
int index = indices[i, j];
|
||||
|
||||
int r;
|
||||
int g;
|
||||
int b;
|
||||
int a;
|
||||
uint r;
|
||||
uint g;
|
||||
uint b;
|
||||
uint a;
|
||||
|
||||
if (indexBits2 == 0)
|
||||
{
|
||||
@@ -756,7 +766,7 @@ internal static class BcHelpers
|
||||
}
|
||||
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
|
||||
@@ -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,8 +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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is for copying a decoded block to the output buffer. It handles the pitch and non-4x4 blocks (if necessary).
|
||||
/// </summary>
|
||||
public static void CopyBufferToOutput<TBufferColor, TBufferChannelValue, TOutputColor, TOutputChannelValue>(Span<TBufferColor> buffer, Span<TOutputColor> output, int width, int height, int w, int h, int blockWidth, int blockHeight)
|
||||
where TBufferChannelValue : unmanaged
|
||||
where TBufferColor : unmanaged, IColor<TBufferChannelValue>
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
int hEnd = int.Min(h + blockHeight, height);
|
||||
int wEnd = int.Min(w + blockWidth, width);
|
||||
for (int i = h; i < hEnd; i++)
|
||||
{
|
||||
int outputOffset = i * width;
|
||||
int bufferOffset = (i - h) * blockWidth;
|
||||
for (int j = w; j < wEnd; j++)
|
||||
{
|
||||
output[outputOffset + j] = buffer[bufferOffset + (j - w)].Convert<TBufferColor, TBufferChannelValue, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -6,80 +6,55 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
{
|
||||
public static class DxtDecoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer of size 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
return DecompressDXT1<ColorBGRA32, byte>(input, width, height, out output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
return DecompressDXT1<ColorBGRA32, byte>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</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, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
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 * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressDXT1<TOutputColor, TOutputChannel>(input, width, height, output);
|
||||
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="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</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, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressDXT1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
return DecompressDXT1<TOutputColor, TOutputChannel>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
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="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</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, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
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);
|
||||
|
||||
@@ -97,17 +72,17 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
int q1 = input[offset + 2] | input[offset + 3] << 8;
|
||||
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, TOutputChannel, byte>(r0, g0, b0);
|
||||
colors[1].SetConvertedChannels<TOutputColor, TOutputChannel, byte>(r1, g1, b1);
|
||||
colors[0].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(r0, g0, b0);
|
||||
colors[1].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(r1, g1, b1);
|
||||
if (q0 > q1)
|
||||
{
|
||||
colors[2].SetConvertedChannels<TOutputColor, TOutputChannel, byte>((byte)((r0 * 2 + r1) / 3), (byte)((g0 * 2 + g1) / 3), (byte)((b0 * 2 + b1) / 3));
|
||||
colors[3].SetConvertedChannels<TOutputColor, TOutputChannel, byte>((byte)((r0 + r1 * 2) / 3), (byte)((g0 + g1 * 2) / 3), (byte)((b0 + b1 * 2) / 3));
|
||||
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].SetConvertedChannels<TOutputColor, TOutputChannel, byte>((byte)((r0 + r1) / 2), (byte)((g0 + g1) / 2), (byte)((b0 + b1) / 2));
|
||||
colors[3].SetBlack<TOutputColor, TOutputChannel>();
|
||||
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);
|
||||
@@ -127,80 +102,55 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
return offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT3 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer of size 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
return DecompressDXT3<ColorBGRA32, byte>(input, width, height, out output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT3 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
return DecompressDXT3<ColorBGRA32, byte>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT3 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</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, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
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 * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressDXT3<TOutputColor, TOutputChannel>(input, width, height, output);
|
||||
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="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</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, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressDXT3<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
return DecompressDXT3<TOutputColor, TOutputChannel>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
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="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</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, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
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.Length, width * height);
|
||||
|
||||
@@ -244,7 +194,7 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
uint d = ToUInt32(input, offset + 12);
|
||||
for (int i = 0; i < 16; i++, d >>= 2)
|
||||
{
|
||||
buffer[i].SetConvertedChannels<TOutputColor, TOutputChannel, byte>(colors[unchecked((int)(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;
|
||||
@@ -258,80 +208,55 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
return offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT5 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer of size 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
return DecompressDXT5<ColorBGRA32, byte>(input, width, height, out output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT5 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
return DecompressDXT5<ColorBGRA32, byte>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT5 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</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, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
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 * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressDXT5<TOutputColor, TOutputChannel>(input, width, height, output);
|
||||
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="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</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, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressDXT5<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
return DecompressDXT5<TOutputColor, TOutputChannel>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
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="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</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, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
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.Length, width * height);
|
||||
|
||||
@@ -390,7 +315,7 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
uint dc = ToUInt32(input, offset + 12);
|
||||
for (int i = 0; i < 16; i++, da >>= 3, dc >>= 2)
|
||||
{
|
||||
buffer[i].SetConvertedChannels<TOutputColor, TOutputChannel, byte>(colors[unchecked((int)(dc & 3))], alphas[unchecked((int)(da & 7))]);
|
||||
buffer[i].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(colors[unchecked((int)(dc & 3))], alphas[unchecked((int)(da & 7))]);
|
||||
}
|
||||
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Etc
|
||||
{
|
||||
public static class EtcDecoder
|
||||
{
|
||||
public static int DecompressETC(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressETC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressETC(input, width, height, output);
|
||||
return DecompressETC<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressETC(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressETC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressETC<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressETC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -19,22 +35,22 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEtc1Block(input.Slice(inputOffset, 8), buf);
|
||||
DecodeEtc1Block(input.Slice(inputOffset, 8), buffer);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,14 +58,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressETC2(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressETC2<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressETC2(input, width, height, output);
|
||||
return DecompressETC2<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressETC2(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressETC2<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressETC2<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressETC2<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -59,22 +88,22 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEtc2Block(input.Slice(inputOffset, 8), buf);
|
||||
DecodeEtc2Block(input.Slice(inputOffset, 8), buffer);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,14 +111,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressETC2A1(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressETC2A1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressETC2A1(input, width, height, output);
|
||||
return DecompressETC2A1<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressETC2A1(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressETC2A1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressETC2A1<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressETC2A1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -99,22 +141,22 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEtc2a1Block(input.Slice(inputOffset, 8), buf);
|
||||
DecodeEtc2a1Block(input.Slice(inputOffset, 8), buffer);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,14 +164,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressETC2A8(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressETC2A8<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressETC2A8(input, width, height, output);
|
||||
return DecompressETC2A8<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressETC2A8(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressETC2A8<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressETC2A8<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressETC2A8<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 16;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -139,23 +194,23 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeEtc2Block(input.Slice(inputOffset + 8, 8), buf);
|
||||
DecodeEtc2a8Block(input.Slice(inputOffset + 0, 8), buf);
|
||||
DecodeEtc2Block(input.Slice(inputOffset + 8, 8), buffer);
|
||||
DecodeEtc2a8Block(input.Slice(inputOffset + 0, 8), buffer);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,14 +218,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressEACRUnsigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressEACRUnsigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressEACRUnsigned(input, width, height, output);
|
||||
return DecompressEACRUnsigned<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressEACRUnsigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressEACRUnsigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressEACRUnsigned<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressEACRUnsigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -180,26 +248,26 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] = 0xFF000000;
|
||||
buffer[i] = ColorRGBA<byte>.Black;
|
||||
}
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset, 8), buf, 2);
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset, 8), buffer, 0);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,14 +275,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressEACRSigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressEACRSigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressEACRSigned(input, width, height, output);
|
||||
return DecompressEACRSigned<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressEACRSigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressEACRSigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressEACRSigned<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressEACRSigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -224,26 +305,26 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] = 0xFF000000;
|
||||
buffer[i] = ColorRGBA<byte>.Black;
|
||||
}
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset, 8), buf, 2);
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset, 8), buffer, 0);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -251,14 +332,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressEACRGUnsigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressEACRGUnsigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressEACRGUnsigned(input, width, height, output);
|
||||
return DecompressEACRGUnsigned<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressEACRGUnsigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressEACRGUnsigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressEACRGUnsigned<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressEACRGUnsigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
|
||||
@@ -268,27 +362,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] = 0xFF000000;
|
||||
buffer[i] = ColorRGBA<byte>.Black;
|
||||
}
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset + 0, 8), buf, 2);
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset + 8, 8), buf, 1);
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset + 0, 8), buffer, 0);
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset + 8, 8), buffer, 1);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,14 +390,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressEACRGSigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
public static int DecompressEACRGSigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressEACRGSigned(input, width, height, output);
|
||||
return DecompressEACRGSigned<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressEACRGSigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressEACRGSigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressEACRGSigned<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
public static int DecompressEACRGSigned<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 16;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
@@ -313,27 +420,27 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
Span<ColorRGBA<byte>> buffer = stackalloc ColorRGBA<byte>[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] = 0xFF000000;
|
||||
buffer[i] = ColorRGBA<byte>.Black;
|
||||
}
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset + 0, 8), buf, 2);
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset + 8, 8), buf, 1);
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset + 0, 8), buffer, 0);
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset + 8, 8), buffer, 1);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
int outputOffset = t * 4 * width + s * 4;
|
||||
Span<TOutputColor> outputPtr = output.Slice(outputOffset);
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buffer[j + 4 * i].Convert<ColorRGBA<byte>, byte, TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -344,7 +451,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
/// <summary>
|
||||
/// Parses an 8-byte Etc1 block from <paramref name="input"/>.
|
||||
/// </summary>
|
||||
private static void DecodeEtc1Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeEtc1Block(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
byte i3 = input[3];
|
||||
ReadOnlySpan<int> code =
|
||||
@@ -393,13 +500,12 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
int cd = code[s];
|
||||
int m = Etc1ModifierTable[cd + index];
|
||||
uint color = ApplicateColor(c, s, m);
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color;
|
||||
output[oi] = ApplicateColor(c, s, m);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeEtc2Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeEtc2Block(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
int j = input[6] << 8 | input[7];
|
||||
int k = input[4] << 8 | input[5];
|
||||
@@ -423,7 +529,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
int ti = input[3] >> 1 & 6 | input[3] & 1;
|
||||
byte d = Etc2DistanceTable[ti];
|
||||
ReadOnlySpan<uint> color_set =
|
||||
ReadOnlySpan<ColorRGBA<byte>> color_set =
|
||||
[
|
||||
ApplicateColorRaw(c, 0),
|
||||
ApplicateColor(c, 1, d),
|
||||
@@ -434,9 +540,8 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
uint color = color_set[index];
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color;
|
||||
output[oi] = color_set[index];
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -464,7 +569,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
++di;
|
||||
}
|
||||
byte d = Etc2DistanceTable[di];
|
||||
ReadOnlySpan<uint> color_set =
|
||||
ReadOnlySpan<ColorRGBA<byte>> color_set =
|
||||
[
|
||||
ApplicateColor(c, 0, d),
|
||||
ApplicateColor(c, 0, -d),
|
||||
@@ -474,9 +579,8 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
uint color = color_set[index];
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color;
|
||||
output[oi] = color_set[index];
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -507,7 +611,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
byte ri = Clamp255((x * (c[1 * 3 + 0] - c[0 * 3 + 0]) + y * (c[2 * 3 + 0] - c[0 * 3 + 0]) + 4 * c[0 * 3 + 0] + 2) >> 2);
|
||||
byte gi = Clamp255((x * (c[1 * 3 + 1] - c[0 * 3 + 1]) + y * (c[2 * 3 + 1] - c[0 * 3 + 1]) + 4 * c[0 * 3 + 1] + 2) >> 2);
|
||||
byte bi = Clamp255((x * (c[1 * 3 + 2] - c[0 * 3 + 2]) + y * (c[2 * 3 + 2] - c[0 * 3 + 2]) + 4 * c[0 * 3 + 2] + 2) >> 2);
|
||||
output[i] = Color(ri, gi, bi, 255);
|
||||
output[i] = new(ri, gi, bi, byte.MaxValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -538,9 +642,8 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
int ci = code[s];
|
||||
int m = Etc1ModifierTable[ci + index];
|
||||
uint color = ApplicateColor(c, s, m);
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color;
|
||||
output[oi] = ApplicateColor(c, s, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -570,15 +673,14 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
int ci = code[s];
|
||||
int m = Etc1ModifierTable[ci + index];
|
||||
uint color = ApplicateColor(c, s, m);
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color;
|
||||
output[oi] = ApplicateColor(c, s, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static void DecodeEtc2a1Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeEtc2a1Block(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
if ((input[3] & 2) != 0)
|
||||
{
|
||||
@@ -591,7 +693,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeEtc2PunchThrowBlock(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeEtc2PunchThrowBlock(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
int j = input[6] << 8 | input[7];
|
||||
int k = input[4] << 8 | input[5];
|
||||
@@ -612,7 +714,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
int ti = input[3] >> 1 & 6 | input[3] & 1;
|
||||
byte d = Etc2DistanceTable[ti];
|
||||
ReadOnlySpan<uint> color_set =
|
||||
ReadOnlySpan<ColorRGBA<byte>> color_set =
|
||||
[
|
||||
ApplicateColorRaw(c, 0),
|
||||
ApplicateColor(c, 1, d),
|
||||
@@ -623,10 +725,8 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
uint color = color_set[index];
|
||||
uint mask = PunchthroughMaskTable[index];
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color & mask;
|
||||
output[oi] = PunchthroughBooleanTable[index] ? color_set[index] : default;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -654,7 +754,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
++di;
|
||||
}
|
||||
byte d = Etc2DistanceTable[di];
|
||||
ReadOnlySpan<uint> color_set =
|
||||
ReadOnlySpan<ColorRGBA<byte>> color_set =
|
||||
[
|
||||
ApplicateColor(c, 0, d),
|
||||
ApplicateColor(c, 0, -d),
|
||||
@@ -664,10 +764,8 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
uint color = color_set[index];
|
||||
uint mask = PunchthroughMaskTable[index];
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color & mask;
|
||||
output[oi] = PunchthroughBooleanTable[index] ? color_set[index] : default;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -698,7 +796,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
byte ri = Clamp255((x * (c[1 * 3 + 0] - c[0 * 3 + 0]) + y * (c[2 * 3 + 0] - c[0 * 3 + 0]) + 4 * c[0 * 3 + 0] + 2) >> 2);
|
||||
byte gi = Clamp255((x * (c[1 * 3 + 1] - c[0 * 3 + 1]) + y * (c[2 * 3 + 1] - c[0 * 3 + 1]) + 4 * c[0 * 3 + 1] + 2) >> 2);
|
||||
byte bi = Clamp255((x * (c[1 * 3 + 2] - c[0 * 3 + 2]) + y * (c[2 * 3 + 2] - c[0 * 3 + 2]) + 4 * c[0 * 3 + 2] + 2) >> 2);
|
||||
output[i] = Color(ri, gi, bi, 255);
|
||||
output[i] = new(ri, gi, bi, byte.MaxValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -725,22 +823,28 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
int s = Etc1SubblockTable[ti + i];
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
int ci = code[s];
|
||||
int m = PunchthroughModifierTable[ci + index];
|
||||
uint color = ApplicateColor(c, s, m);
|
||||
uint mask = PunchthroughMaskTable[index];
|
||||
int oi = WriteOrderTable[i];
|
||||
output[oi] = color & mask;
|
||||
if (PunchthroughBooleanTable[index])
|
||||
{
|
||||
int s = Etc1SubblockTable[ti + i];
|
||||
int ci = code[s];
|
||||
int m = PunchthroughModifierTable[ci + index];
|
||||
output[oi] = ApplicateColor(c, s, m);
|
||||
}
|
||||
else
|
||||
{
|
||||
output[oi] = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeEtc2a8Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
private static void DecodeEtc2a8Block(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output)
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
int @base = input[0];
|
||||
int data1 = input[1];
|
||||
int mul = data1 >> 4;
|
||||
@@ -748,48 +852,50 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(3), @base);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<ColorRGBA<byte>, byte>(output).Slice(3), @base);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int table = data1 & 0xF;
|
||||
ulong l = Get6SwapedBytes(input);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(3), @base, table, mul, l);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<ColorRGBA<byte>, byte>(output).Slice(3), @base, table, mul, l);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeEacUnsignedBlock(ReadOnlySpan<byte> input, Span<uint> output, int channel)
|
||||
private static void DecodeEacUnsignedBlock(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output, int channel)
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
int @base = input[0];
|
||||
int data1 = input[1];
|
||||
int mul = data1 >> 4;
|
||||
if (mul == 0)
|
||||
{
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<ColorRGBA<byte>, byte>(output).Slice(channel), @base);
|
||||
}
|
||||
else
|
||||
{
|
||||
int table = data1 & 0xF;
|
||||
ulong l = Get6SwapedBytes(input);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base, table, mul, l);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<ColorRGBA<byte>, byte>(output).Slice(channel), @base, table, mul, l);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeEacSignedBlock(ReadOnlySpan<byte> input, Span<uint> output, int channel)
|
||||
private static void DecodeEacSignedBlock(ReadOnlySpan<byte> input, Span<ColorRGBA<byte>> output, int channel)
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
int @base = 127 + unchecked((sbyte)input[0]);
|
||||
int data1 = input[1];
|
||||
int mul = data1 >> 4;
|
||||
if (mul == 0)
|
||||
{
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<ColorRGBA<byte>, byte>(output).Slice(channel), @base);
|
||||
}
|
||||
else
|
||||
{
|
||||
int table = data1 & 0xF;
|
||||
ulong l = Get6SwapedBytes(input);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base, table, mul, l);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<ColorRGBA<byte>, byte>(output).Slice(channel), @base, table, mul, l);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -805,7 +911,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static void DecodeEac11Block(Span<byte> output, int @base, int ti, int mul, ulong l)
|
||||
{
|
||||
ReadOnlySpan<sbyte> table = Etc2AlphaModTable.AsSpan(ti * 8, 8);
|
||||
ReadOnlySpan<sbyte> table = Etc2AlphaModTable.Slice(ti * 8, 8);
|
||||
for (int i = 0; i < 16; i++, l >>= 3)
|
||||
{
|
||||
int val = @base + mul * table[unchecked((int)(l & 0b111))];
|
||||
@@ -813,12 +919,6 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static uint Color(int r, int g, int b, int a)
|
||||
{
|
||||
return unchecked((uint)(r << 16 | g << 8 | b | a << 24));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static byte Clamp255(int n)
|
||||
{
|
||||
@@ -826,21 +926,21 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static uint ApplicateColor(ReadOnlySpan<byte> c, int o, int m)
|
||||
private static ColorRGBA<byte> ApplicateColor(ReadOnlySpan<byte> c, int o, int m)
|
||||
{
|
||||
return Color(Clamp255(c[o * 3 + 0] + m), Clamp255(c[o * 3 + 1] + m), Clamp255(c[o * 3 + 2] + m), 255);
|
||||
return new(Clamp255(c[o * 3 + 0] + m), Clamp255(c[o * 3 + 1] + m), Clamp255(c[o * 3 + 2] + m), byte.MaxValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static uint ApplicateColor(ReadOnlySpan<int> c, int o, int m)
|
||||
private static ColorRGBA<byte> ApplicateColor(ReadOnlySpan<int> c, int o, int m)
|
||||
{
|
||||
return Color(Clamp255(c[o * 3 + 0] + m), Clamp255(c[o * 3 + 1] + m), Clamp255(c[o * 3 + 2] + m), 255);
|
||||
return new(Clamp255(c[o * 3 + 0] + m), Clamp255(c[o * 3 + 1] + m), Clamp255(c[o * 3 + 2] + m), byte.MaxValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static uint ApplicateColorRaw(ReadOnlySpan<byte> c, int o)
|
||||
private static ColorRGBA<byte> ApplicateColorRaw(ReadOnlySpan<byte> c, int o)
|
||||
{
|
||||
return Color(c[o * 3 + 0], c[o * 3 + 1], c[o * 3 + 2], 255);
|
||||
return new(c[o * 3 + 0], c[o * 3 + 1], c[o * 3 + 2], byte.MaxValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
@@ -849,14 +949,14 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return data[7] | (uint)data[6] << 8 | (uint)data[5] << 16 | (uint)data[4] << 24 | (ulong)data[3] << 32 | (ulong)data[2] << 40;
|
||||
}
|
||||
|
||||
private static readonly byte[] WriteOrderTable = [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15];
|
||||
private static readonly byte[] WriteOrderTableRev = [15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0];
|
||||
private static readonly int[] Etc1SubblockTable =
|
||||
private static ReadOnlySpan<byte> WriteOrderTable => [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15];
|
||||
private static ReadOnlySpan<byte> WriteOrderTableRev => [15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0];
|
||||
private static ReadOnlySpan<int> Etc1SubblockTable =>
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
|
||||
];
|
||||
private static readonly int[] Etc1ModifierTable =
|
||||
private static ReadOnlySpan<int> Etc1ModifierTable =>
|
||||
[
|
||||
2, 8, -2, -8,
|
||||
5, 17, -5, -17,
|
||||
@@ -867,7 +967,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
33, 106, -33, -106,
|
||||
47, 183, -47, -183,
|
||||
];
|
||||
private static readonly int[] PunchthroughModifierTable =
|
||||
private static ReadOnlySpan<int> PunchthroughModifierTable =>
|
||||
[
|
||||
0, 8, 0, -8,
|
||||
0, 17, 0, -17,
|
||||
@@ -878,8 +978,8 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
0, 106, 0, -106,
|
||||
0, 183, 0, -183,
|
||||
];
|
||||
private static readonly byte[] Etc2DistanceTable = [3, 6, 11, 16, 23, 32, 41, 64];
|
||||
private static readonly sbyte[] Etc2AlphaModTable =
|
||||
private static ReadOnlySpan<byte> Etc2DistanceTable => [3, 6, 11, 16, 23, 32, 41, 64];
|
||||
private static ReadOnlySpan<sbyte> Etc2AlphaModTable =>
|
||||
[
|
||||
-3, -6, -9, -15, 2, 5, 8, 14,
|
||||
-3, -7, -10, -13, 2, 6, 9, 12,
|
||||
@@ -898,6 +998,6 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
-4, -6, -8, -9, 3, 5, 7, 8,
|
||||
-3, -5, -7, -9, 2, 4, 6, 8,
|
||||
];
|
||||
private static readonly uint[] PunchthroughMaskTable = [0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF];
|
||||
private static ReadOnlySpan<bool> PunchthroughBooleanTable => [true, true, false, true];
|
||||
}
|
||||
}
|
||||
@@ -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) + sizeof(byte);
|
||||
|
||||
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) + 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);
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,65 @@
|
||||
//#define DISABLE_TWINDDLING_ROUTINE
|
||||
#define ASSUME_IMAGE_TILING
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
{
|
||||
public static partial class PvrtcDecoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Decompresses PVRTC to RGBA 8888
|
||||
/// Decompresses PVRTC
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">The PVRTC texture data to decompress</param>
|
||||
/// <param name="xDim">X dimension (width) of the texture</param>
|
||||
/// <param name="yDim">Y dimension (height) of the texture</param>
|
||||
/// <param name="output">The decompressed texture data</param>
|
||||
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
|
||||
/// <returns>The number of bytes read from <paramref name="input"/></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static int DecompressPVRTC(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, out byte[] output)
|
||||
public static int DecompressPVRTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[xDim * yDim * sizeof(uint)];
|
||||
return DecompressPVRTC(input, xDim, yDim, do2bitMode, output);
|
||||
output = new byte[xDim * yDim * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressPVRTC<TOutputColor, TOutputChannelValue>(input, xDim, yDim, do2bitMode, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses PVRTC to RGBA 8888
|
||||
/// Decompresses PVRTC
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">The PVRTC texture data to decompress</param>
|
||||
/// <param name="xDim">X dimension (width) of the texture</param>
|
||||
/// <param name="yDim">Y dimension (height) of the texture</param>
|
||||
/// <param name="output">The decompressed texture data</param>
|
||||
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
|
||||
/// <returns>The number of bytes read from <paramref name="input"/></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static int DecompressPVRTC(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, Span<byte> output)
|
||||
public static int DecompressPVRTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
return DecompressPVRTC<TOutputColor, TOutputChannelValue>(input, xDim, yDim, do2bitMode, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses PVRTC
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">The PVRTC texture data to decompress</param>
|
||||
/// <param name="xDim">X dimension (width) of the texture</param>
|
||||
/// <param name="yDim">Y dimension (height) of the texture</param>
|
||||
/// <param name="output">The decompressed texture data</param>
|
||||
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
|
||||
/// <returns>The number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressPVRTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotLittleEndian();
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, xDim * yDim);
|
||||
int xBlockSize = do2bitMode ? BlockX2bpp : BlockX4bpp;
|
||||
// for MBX don't allow the sizes to get too small
|
||||
int blockXDim = Math.Max(2, xDim / xBlockSize);
|
||||
@@ -65,12 +92,12 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
int blockX = (x - xBlockSize / 2);
|
||||
int blockY = (y - BlockYSize / 2);
|
||||
|
||||
blockX = LimitCoord(blockX, xDim) / xBlockSize;
|
||||
blockY = LimitCoord(blockY, yDim) / BlockYSize;
|
||||
blockX = LimitCoordinate(blockX, xDim) / xBlockSize;
|
||||
blockY = LimitCoordinate(blockY, yDim) / BlockYSize;
|
||||
|
||||
// compute the positions of the other 3 blocks
|
||||
int blockXp1 = LimitCoord(blockX + 1, blockXDim);
|
||||
int blockYp1 = LimitCoord(blockY + 1, blockYDim);
|
||||
int blockXp1 = LimitCoordinate(blockX + 1, blockXDim);
|
||||
int blockYp1 = LimitCoordinate(blockY + 1, blockYDim);
|
||||
|
||||
// map to block memory locations
|
||||
uint blocki00 = TwiddleUV((uint)blockYDim, (uint)blockXDim, (uint)blockY, (uint)blockX);
|
||||
@@ -108,12 +135,12 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
InterpolateColours(m_colors[0], m_colors[1], m_colors[2], m_colors[3], 1, do2bitMode, x, y, bSig);
|
||||
GetModulationValue(x, y, do2bitMode, modulationVals, modulationModes, out int mod, out bool doPT);
|
||||
|
||||
// compute the modulated color. Swap red and blue channel
|
||||
int position = (x + y * xDim) << 2;
|
||||
output[position + 0] = (byte)((aSig[2] * 8 + mod * (bSig[2] - aSig[2])) >> 3);
|
||||
output[position + 1] = (byte)((aSig[1] * 8 + mod * (bSig[1] - aSig[1])) >> 3);
|
||||
output[position + 2] = (byte)((aSig[0] * 8 + mod * (bSig[0] - aSig[0])) >> 3);
|
||||
output[position + 3] = doPT ? (byte)0 : (byte)((aSig[3] * 8 + mod * (bSig[3] - aSig[3])) >> 3);
|
||||
// compute the modulated color.
|
||||
byte red = (byte)((aSig[0] * 8 + mod * (bSig[0] - aSig[0])) >> 3);
|
||||
byte green = (byte)((aSig[1] * 8 + mod * (bSig[1] - aSig[1])) >> 3);
|
||||
byte blue = (byte)((aSig[2] * 8 + mod * (bSig[2] - aSig[2])) >> 3);
|
||||
byte alpha = doPT ? (byte)0 : (byte)((aSig[3] * 8 + mod * (bSig[3] - aSig[3])) >> 3);
|
||||
output[x + y * xDim].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(red, green, blue, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,24 +159,10 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
/// <returns>The twiddled offset of the pixel</returns>
|
||||
private static uint TwiddleUV(uint ySize, uint xSize, uint yPos, uint xPos)
|
||||
{
|
||||
#if DEBUG
|
||||
if (yPos >= ySize)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
if (xPos >= xSize)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
if (!BitOperations.IsPow2(ySize))
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
if (!BitOperations.IsPow2(xSize))
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
#endif
|
||||
Debug.Assert(yPos < ySize);
|
||||
Debug.Assert(xPos < xSize);
|
||||
Debug.Assert(BitOperations.IsPow2(ySize));
|
||||
Debug.Assert(BitOperations.IsPow2(xSize));
|
||||
|
||||
uint minDimension;
|
||||
uint maxValue;
|
||||
@@ -164,10 +177,6 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
maxValue = yPos;
|
||||
}
|
||||
|
||||
#if DISABLE_TWINDDLING_ROUTINE
|
||||
// nasty hack to disable twiddling
|
||||
return (yPos * xSize + xPos);
|
||||
#else
|
||||
// step through all the bits in the "minimum" dimension
|
||||
uint srcBitPos = 1;
|
||||
uint dstBitPos = 1;
|
||||
@@ -195,7 +204,6 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
maxValue >>= shiftCount;
|
||||
twiddled |= (maxValue << (2 * shiftCount));
|
||||
return twiddled;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -217,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;
|
||||
}
|
||||
|
||||
@@ -290,16 +298,11 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// sanity check
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (result[i] >= 256)
|
||||
{
|
||||
throw new Exception("Sanity failed");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Debug.Assert(result[0] < 256);
|
||||
Debug.Assert(result[1] < 256);
|
||||
Debug.Assert(result[2] < 256);
|
||||
Debug.Assert(result[3] < 256);
|
||||
|
||||
// convert from 5554 to 8888, do RGB 5.3 => 8
|
||||
for (int i = 0; i < 3; i++)
|
||||
@@ -308,16 +311,11 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
}
|
||||
result[3] += result[3] >> 4;
|
||||
|
||||
#if DEBUG
|
||||
// 2nd sanity check
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (result[i] >= 256)
|
||||
{
|
||||
throw new Exception("2nd sanity failed");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Debug.Assert(result[0] < 256);
|
||||
Debug.Assert(result[1] < 256);
|
||||
Debug.Assert(result[2] < 256);
|
||||
Debug.Assert(result[3] < 256);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -456,19 +454,20 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Define an expression to either wrap or clamp large or small vals to the legal coordinate range
|
||||
/// Define an expression to either wrap or clamp large or small values to the legal coordinate range.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static int LimitCoord(int value, int size)
|
||||
private static int LimitCoordinate(int value, int size, bool assumeImageTiling = true)
|
||||
{
|
||||
#if ASSUME_IMAGE_TILING
|
||||
// wrap coord
|
||||
return value & (size - 1);
|
||||
#else
|
||||
// clamp
|
||||
return h < x ? h : (x > l ? x : l);
|
||||
return Clamp(Val, 0, Size - 1);
|
||||
#endif
|
||||
if (assumeImageTiling)
|
||||
{
|
||||
// wrap coordinate
|
||||
return value & (size - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// clamp coordinate
|
||||
return int.Clamp(value, 0, size - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -487,7 +486,7 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
private const int BlockX2bpp = 8;
|
||||
private const int BlockX4bpp = 4;
|
||||
|
||||
private static readonly int[] m_repVals0 = [0, 3, 5, 8];
|
||||
private static readonly int[] m_repVals1 = [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,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;
|
||||
}
|
||||
@@ -74,22 +74,48 @@ public static class Color
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static TTarget Convert<TThis, TThisChannel, TTarget, TTargetChannel>(this TThis color)
|
||||
where TThisChannel : unmanaged
|
||||
where TTargetChannel : unmanaged
|
||||
where TThis : unmanaged, IColor<TThisChannel>
|
||||
where TTarget : unmanaged, IColor<TTargetChannel>
|
||||
public static TTarget Convert<TThis, TThisChannelValue, TTarget, TTargetChannelValue>(this TThis color)
|
||||
where TThisChannelValue : unmanaged
|
||||
where TTargetChannelValue : unmanaged
|
||||
where TThis : unmanaged, IColor<TThisChannelValue>
|
||||
where TTarget : unmanaged, IColor<TTargetChannelValue>
|
||||
{
|
||||
if (typeof(TThis) == typeof(TTarget))
|
||||
{
|
||||
return Unsafe.As<TThis, TTarget>(ref color);
|
||||
return Unsafe.BitCast<TThis, TTarget>(color);
|
||||
}
|
||||
else
|
||||
{
|
||||
TTarget destination = default;
|
||||
color.GetChannels(out TThisChannel r, out TThisChannel g, out TThisChannel b, out TThisChannel a);
|
||||
destination.SetConvertedChannels<TTarget, TTargetChannel, TThisChannel>(r, g, b, a);
|
||||
color.GetChannels(out TThisChannelValue r, out TThisChannelValue g, out TThisChannelValue b, out TThisChannelValue a);
|
||||
destination.SetConvertedChannels<TTarget, TTargetChannelValue, TThisChannelValue>(r, g, b, a);
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
|
||||
internal static TColor SRgbToLinear<TColor, TChannel>(this TColor color)
|
||||
where TChannel : unmanaged
|
||||
where TColor : unmanaged, IColor<TChannel>
|
||||
{
|
||||
color.GetChannels(out TChannel r, out TChannel g, out TChannel b, out TChannel a);
|
||||
color.SetChannels(
|
||||
SRgb.ToLinear(r),
|
||||
SRgb.ToLinear(g),
|
||||
SRgb.ToLinear(b),
|
||||
a);
|
||||
return color;
|
||||
}
|
||||
|
||||
internal static TColor LinearToSRgb<TColor, TChannel>(this TColor color)
|
||||
where TChannel : unmanaged
|
||||
where TColor : unmanaged, IColor<TChannel>
|
||||
{
|
||||
color.GetChannels(out TChannel r, out TChannel g, out TChannel b, out TChannel a);
|
||||
color.SetChannels(
|
||||
SRgb.FromLinear(r),
|
||||
SRgb.FromLinear(g),
|
||||
SRgb.FromLinear(b),
|
||||
a);
|
||||
return color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
public partial struct Color<TChannelValue, TChannel> : IColor<Color<TChannelValue, TChannel>, TChannelValue>
|
||||
where TChannelValue : unmanaged, INumberBase<TChannelValue>, IMinMaxValue<TChannelValue>
|
||||
where TChannel : IChannel
|
||||
{
|
||||
private TChannelValue value;
|
||||
|
||||
public TChannelValue R
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel.IsRed)
|
||||
{
|
||||
return TChannel.GetRed(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfRed<TChannel, TChannelValue>(ref value, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue G
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel.IsGreen)
|
||||
{
|
||||
return TChannel.GetGreen(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfGreen<TChannel, TChannelValue>(ref value, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue B
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel.IsBlue)
|
||||
{
|
||||
return TChannel.GetBlue(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfBlue<TChannel, TChannelValue>(ref value, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue A
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel.IsAlpha)
|
||||
{
|
||||
return TChannel.GetAlpha(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMaximumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfAlpha<TChannel, TChannelValue>(ref value, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Color(TChannelValue value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out TChannelValue r, out TChannelValue g, out TChannelValue b, out TChannelValue a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(TChannelValue r, TChannelValue g, TChannelValue b, TChannelValue a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => TChannel.IsRed;
|
||||
static bool IColor.HasGreenChannel => TChannel.IsGreen;
|
||||
static bool IColor.HasBlueChannel => TChannel.IsBlue;
|
||||
static bool IColor.HasAlphaChannel => TChannel.IsAlpha;
|
||||
static bool IColor.ChannelsAreFullyUtilized => TChannel.FullyUtilized;
|
||||
static Type IColor.ChannelType => typeof(TChannelValue);
|
||||
|
||||
public static Color<TChannelValue, TChannel> Black
|
||||
{
|
||||
get => new(TChannel.GetBlack<TChannelValue>());
|
||||
}
|
||||
|
||||
public static Color<TChannelValue, TChannel> White
|
||||
{
|
||||
get => new(TChannel.GetWhite<TChannelValue>());
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
public partial struct Color<TChannelValue, TChannel1, TChannel2> : IColor<Color<TChannelValue, TChannel1, TChannel2>, TChannelValue>
|
||||
where TChannelValue : unmanaged, INumberBase<TChannelValue>, IMinMaxValue<TChannelValue>
|
||||
where TChannel1 : IChannel
|
||||
where TChannel2 : IChannel
|
||||
{
|
||||
private TChannelValue value1;
|
||||
private TChannelValue value2;
|
||||
|
||||
public TChannelValue R
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsRed)
|
||||
{
|
||||
return TChannel1.GetRed(value1);
|
||||
}
|
||||
else if (TChannel2.IsRed)
|
||||
{
|
||||
return TChannel2.GetRed(value2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfRed<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfRed<TChannel2, TChannelValue>(ref value2, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue G
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsGreen)
|
||||
{
|
||||
return TChannel1.GetGreen(value1);
|
||||
}
|
||||
else if (TChannel2.IsGreen)
|
||||
{
|
||||
return TChannel2.GetGreen(value2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfGreen<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfGreen<TChannel2, TChannelValue>(ref value2, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue B
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsBlue)
|
||||
{
|
||||
return TChannel1.GetBlue(value1);
|
||||
}
|
||||
else if (TChannel2.IsBlue)
|
||||
{
|
||||
return TChannel2.GetBlue(value2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfBlue<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfBlue<TChannel2, TChannelValue>(ref value2, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue A
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsAlpha)
|
||||
{
|
||||
return TChannel1.GetAlpha(value1);
|
||||
}
|
||||
else if (TChannel2.IsAlpha)
|
||||
{
|
||||
return TChannel2.GetAlpha(value2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMaximumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfAlpha<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfAlpha<TChannel2, TChannelValue>(ref value2, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Color(TChannelValue value1, TChannelValue value2)
|
||||
{
|
||||
this.value1 = value1;
|
||||
this.value2 = value2;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out TChannelValue r, out TChannelValue g, out TChannelValue b, out TChannelValue a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(TChannelValue r, TChannelValue g, TChannelValue b, TChannelValue a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => TChannel1.IsRed || TChannel2.IsRed;
|
||||
static bool IColor.HasGreenChannel => TChannel1.IsGreen || TChannel2.IsGreen;
|
||||
static bool IColor.HasBlueChannel => TChannel1.IsBlue || TChannel2.IsBlue;
|
||||
static bool IColor.HasAlphaChannel => TChannel1.IsAlpha || TChannel2.IsAlpha;
|
||||
static bool IColor.ChannelsAreFullyUtilized => TChannel1.FullyUtilized && TChannel2.FullyUtilized;
|
||||
static Type IColor.ChannelType => typeof(TChannelValue);
|
||||
|
||||
public static Color<TChannelValue, TChannel1, TChannel2> Black
|
||||
{
|
||||
get => new(TChannel1.GetBlack<TChannelValue>(), TChannel2.GetBlack<TChannelValue>());
|
||||
}
|
||||
|
||||
public static Color<TChannelValue, TChannel1, TChannel2> White
|
||||
{
|
||||
get => new(TChannel1.GetWhite<TChannelValue>(), TChannel2.GetWhite<TChannelValue>());
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
public partial struct Color<TChannelValue, TChannel1, TChannel2, TChannel3> : IColor<Color<TChannelValue, TChannel1, TChannel2, TChannel3>, TChannelValue>
|
||||
where TChannelValue : unmanaged, INumberBase<TChannelValue>, IMinMaxValue<TChannelValue>
|
||||
where TChannel1 : IChannel
|
||||
where TChannel2 : IChannel
|
||||
where TChannel3 : IChannel
|
||||
{
|
||||
private TChannelValue value1;
|
||||
private TChannelValue value2;
|
||||
private TChannelValue value3;
|
||||
|
||||
public TChannelValue R
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsRed)
|
||||
{
|
||||
return TChannel1.GetRed(value1);
|
||||
}
|
||||
else if (TChannel2.IsRed)
|
||||
{
|
||||
return TChannel2.GetRed(value2);
|
||||
}
|
||||
else if (TChannel3.IsRed)
|
||||
{
|
||||
return TChannel3.GetRed(value3);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfRed<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfRed<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfRed<TChannel3, TChannelValue>(ref value3, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue G
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsGreen)
|
||||
{
|
||||
return TChannel1.GetGreen(value1);
|
||||
}
|
||||
else if (TChannel2.IsGreen)
|
||||
{
|
||||
return TChannel2.GetGreen(value2);
|
||||
}
|
||||
else if (TChannel3.IsGreen)
|
||||
{
|
||||
return TChannel3.GetGreen(value3);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfGreen<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfGreen<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfGreen<TChannel3, TChannelValue>(ref value3, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue B
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsBlue)
|
||||
{
|
||||
return TChannel1.GetBlue(value1);
|
||||
}
|
||||
else if (TChannel2.IsBlue)
|
||||
{
|
||||
return TChannel2.GetBlue(value2);
|
||||
}
|
||||
else if (TChannel3.IsBlue)
|
||||
{
|
||||
return TChannel3.GetBlue(value3);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfBlue<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfBlue<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfBlue<TChannel3, TChannelValue>(ref value3, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue A
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsAlpha)
|
||||
{
|
||||
return TChannel1.GetAlpha(value1);
|
||||
}
|
||||
else if (TChannel2.IsAlpha)
|
||||
{
|
||||
return TChannel2.GetAlpha(value2);
|
||||
}
|
||||
else if (TChannel3.IsAlpha)
|
||||
{
|
||||
return TChannel3.GetAlpha(value3);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMaximumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfAlpha<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfAlpha<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfAlpha<TChannel3, TChannelValue>(ref value3, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Color(TChannelValue value1, TChannelValue value2, TChannelValue value3)
|
||||
{
|
||||
this.value1 = value1;
|
||||
this.value2 = value2;
|
||||
this.value3 = value3;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out TChannelValue r, out TChannelValue g, out TChannelValue b, out TChannelValue a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(TChannelValue r, TChannelValue g, TChannelValue b, TChannelValue a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => TChannel1.IsRed || TChannel2.IsRed || TChannel3.IsRed;
|
||||
static bool IColor.HasGreenChannel => TChannel1.IsGreen || TChannel2.IsGreen || TChannel3.IsGreen;
|
||||
static bool IColor.HasBlueChannel => TChannel1.IsBlue || TChannel2.IsBlue || TChannel3.IsBlue;
|
||||
static bool IColor.HasAlphaChannel => TChannel1.IsAlpha || TChannel2.IsAlpha || TChannel3.IsAlpha;
|
||||
static bool IColor.ChannelsAreFullyUtilized => TChannel1.FullyUtilized && TChannel2.FullyUtilized && TChannel3.FullyUtilized;
|
||||
static Type IColor.ChannelType => typeof(TChannelValue);
|
||||
|
||||
public static Color<TChannelValue, TChannel1, TChannel2, TChannel3> Black
|
||||
{
|
||||
get => new(TChannel1.GetBlack<TChannelValue>(), TChannel2.GetBlack<TChannelValue>(), TChannel3.GetBlack<TChannelValue>());
|
||||
}
|
||||
|
||||
public static Color<TChannelValue, TChannel1, TChannel2, TChannel3> White
|
||||
{
|
||||
get => new(TChannel1.GetWhite<TChannelValue>(), TChannel2.GetWhite<TChannelValue>(), TChannel3.GetWhite<TChannelValue>());
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
public partial struct Color<TChannelValue, TChannel1, TChannel2, TChannel3, TChannel4> : IColor<Color<TChannelValue, TChannel1, TChannel2, TChannel3, TChannel4>, TChannelValue>
|
||||
where TChannelValue : unmanaged, INumberBase<TChannelValue>, IMinMaxValue<TChannelValue>
|
||||
where TChannel1 : IChannel
|
||||
where TChannel2 : IChannel
|
||||
where TChannel3 : IChannel
|
||||
where TChannel4 : IChannel
|
||||
{
|
||||
private TChannelValue value1;
|
||||
private TChannelValue value2;
|
||||
private TChannelValue value3;
|
||||
private TChannelValue value4;
|
||||
|
||||
public TChannelValue R
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsRed)
|
||||
{
|
||||
return TChannel1.GetRed(value1);
|
||||
}
|
||||
else if (TChannel2.IsRed)
|
||||
{
|
||||
return TChannel2.GetRed(value2);
|
||||
}
|
||||
else if (TChannel3.IsRed)
|
||||
{
|
||||
return TChannel3.GetRed(value3);
|
||||
}
|
||||
else if (TChannel4.IsRed)
|
||||
{
|
||||
return TChannel4.GetRed(value4);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfRed<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfRed<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfRed<TChannel3, TChannelValue>(ref value3, value);
|
||||
Channel.SetIfRed<TChannel4, TChannelValue>(ref value4, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue G
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsGreen)
|
||||
{
|
||||
return TChannel1.GetGreen(value1);
|
||||
}
|
||||
else if (TChannel2.IsGreen)
|
||||
{
|
||||
return TChannel2.GetGreen(value2);
|
||||
}
|
||||
else if (TChannel3.IsGreen)
|
||||
{
|
||||
return TChannel3.GetGreen(value3);
|
||||
}
|
||||
else if (TChannel4.IsGreen)
|
||||
{
|
||||
return TChannel4.GetGreen(value4);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfGreen<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfGreen<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfGreen<TChannel3, TChannelValue>(ref value3, value);
|
||||
Channel.SetIfGreen<TChannel4, TChannelValue>(ref value4, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue B
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsBlue)
|
||||
{
|
||||
return TChannel1.GetBlue(value1);
|
||||
}
|
||||
else if (TChannel2.IsBlue)
|
||||
{
|
||||
return TChannel2.GetBlue(value2);
|
||||
}
|
||||
else if (TChannel3.IsBlue)
|
||||
{
|
||||
return TChannel3.GetBlue(value3);
|
||||
}
|
||||
else if (TChannel4.IsBlue)
|
||||
{
|
||||
return TChannel4.GetBlue(value4);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfBlue<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfBlue<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfBlue<TChannel3, TChannelValue>(ref value3, value);
|
||||
Channel.SetIfBlue<TChannel4, TChannelValue>(ref value4, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue A
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsAlpha)
|
||||
{
|
||||
return TChannel1.GetAlpha(value1);
|
||||
}
|
||||
else if (TChannel2.IsAlpha)
|
||||
{
|
||||
return TChannel2.GetAlpha(value2);
|
||||
}
|
||||
else if (TChannel3.IsAlpha)
|
||||
{
|
||||
return TChannel3.GetAlpha(value3);
|
||||
}
|
||||
else if (TChannel4.IsAlpha)
|
||||
{
|
||||
return TChannel4.GetAlpha(value4);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMaximumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfAlpha<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfAlpha<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfAlpha<TChannel3, TChannelValue>(ref value3, value);
|
||||
Channel.SetIfAlpha<TChannel4, TChannelValue>(ref value4, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Color(TChannelValue value1, TChannelValue value2, TChannelValue value3, TChannelValue value4)
|
||||
{
|
||||
this.value1 = value1;
|
||||
this.value2 = value2;
|
||||
this.value3 = value3;
|
||||
this.value4 = value4;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out TChannelValue r, out TChannelValue g, out TChannelValue b, out TChannelValue a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(TChannelValue r, TChannelValue g, TChannelValue b, TChannelValue a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => TChannel1.IsRed || TChannel2.IsRed || TChannel3.IsRed || TChannel4.IsRed;
|
||||
static bool IColor.HasGreenChannel => TChannel1.IsGreen || TChannel2.IsGreen || TChannel3.IsGreen || TChannel4.IsGreen;
|
||||
static bool IColor.HasBlueChannel => TChannel1.IsBlue || TChannel2.IsBlue || TChannel3.IsBlue || TChannel4.IsBlue;
|
||||
static bool IColor.HasAlphaChannel => TChannel1.IsAlpha || TChannel2.IsAlpha || TChannel3.IsAlpha || TChannel4.IsAlpha;
|
||||
static bool IColor.ChannelsAreFullyUtilized => TChannel1.FullyUtilized && TChannel2.FullyUtilized && TChannel3.FullyUtilized && TChannel4.FullyUtilized;
|
||||
static Type IColor.ChannelType => typeof(TChannelValue);
|
||||
|
||||
public static Color<TChannelValue, TChannel1, TChannel2, TChannel3, TChannel4> Black
|
||||
{
|
||||
get => new(TChannel1.GetBlack<TChannelValue>(), TChannel2.GetBlack<TChannelValue>(), TChannel3.GetBlack<TChannelValue>(), TChannel4.GetBlack<TChannelValue>());
|
||||
}
|
||||
|
||||
public static Color<TChannelValue, TChannel1, TChannel2, TChannel3, TChannel4> White
|
||||
{
|
||||
get => new(TChannel1.GetWhite<TChannelValue>(), TChannel2.GetWhite<TChannelValue>(), TChannel3.GetWhite<TChannelValue>(), TChannel4.GetWhite<TChannelValue>());
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ namespace AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
public partial struct ColorA<T> : IColor<ColorA<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
public T A { get; set; }
|
||||
|
||||
public readonly T R
|
||||
{
|
||||
get => NumericConversion.GetMinimumValueSafe<T>();
|
||||
@@ -22,8 +24,6 @@ public partial struct ColorA<T> : IColor<ColorA<T>, T> where T : unmanaged, INum
|
||||
set { }
|
||||
}
|
||||
|
||||
public T A { get; set; }
|
||||
|
||||
public ColorA(T a)
|
||||
{
|
||||
A = a;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
public partial struct ColorARGB<T> : IColor<ColorARGB<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
public T A { get; set; }
|
||||
|
||||
public T R { get; set; }
|
||||
|
||||
public T G { get; set; }
|
||||
|
||||
public T B { get; set; }
|
||||
|
||||
public ColorARGB(T r, T g, T b, T a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out T r, out T g, out T b, out T a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(T r, T g, T b, T a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => true;
|
||||
static bool IColor.HasBlueChannel => true;
|
||||
static bool IColor.HasAlphaChannel => true;
|
||||
static bool IColor.ChannelsAreFullyUtilized => true;
|
||||
static Type IColor.ChannelType => typeof(T);
|
||||
|
||||
public static ColorARGB<T> Black => new(NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
|
||||
public static ColorARGB<T> White => new(NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public partial struct ColorARGB32 : IColor<byte>
|
||||
{
|
||||
public byte A { get; set; }
|
||||
public byte R { get; set; }
|
||||
public byte G { get; set; }
|
||||
public byte B { get; set; }
|
||||
|
||||
public readonly void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public partial struct ColorARGB32 : IColor<byte>
|
||||
{
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => true;
|
||||
static bool IColor.HasBlueChannel => true;
|
||||
static bool IColor.HasAlphaChannel => true;
|
||||
static bool IColor.ChannelsAreFullyUtilized => true;
|
||||
static Type IColor.ChannelType => typeof(byte);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
public partial struct ColorBGR<T> : IColor<ColorBGR<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
public T B { get; set; }
|
||||
|
||||
public T G { get; set; }
|
||||
|
||||
public T R { get; set; }
|
||||
|
||||
public readonly T A
|
||||
{
|
||||
get => NumericConversion.GetMaximumValueSafe<T>();
|
||||
set { }
|
||||
}
|
||||
|
||||
public ColorBGR(T r, T g, T b)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out T r, out T g, out T b, out T a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(T r, T g, T b, T a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => true;
|
||||
static bool IColor.HasBlueChannel => true;
|
||||
static bool IColor.HasAlphaChannel => false;
|
||||
static bool IColor.ChannelsAreFullyUtilized => true;
|
||||
static Type IColor.ChannelType => typeof(T);
|
||||
|
||||
public static ColorBGR<T> Black => new(NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>());
|
||||
public static ColorBGR<T> White => new(NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B} }}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
public partial struct ColorBGRA<T> : IColor<ColorBGRA<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
public T B { get; set; }
|
||||
|
||||
public T G { get; set; }
|
||||
|
||||
public T R { get; set; }
|
||||
|
||||
public T A { get; set; }
|
||||
|
||||
public ColorBGRA(T r, T g, T b, T a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out T r, out T g, out T b, out T a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(T r, T g, T b, T a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => true;
|
||||
static bool IColor.HasBlueChannel => true;
|
||||
static bool IColor.HasAlphaChannel => true;
|
||||
static bool IColor.ChannelsAreFullyUtilized => true;
|
||||
static Type IColor.ChannelType => typeof(T);
|
||||
|
||||
public static ColorBGRA<T> Black => new(NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
|
||||
public static ColorBGRA<T> White => new(NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public partial struct ColorBGRA32 : IColor<byte>
|
||||
{
|
||||
public byte B { get; set; }
|
||||
public byte G { get; set; }
|
||||
public byte R { get; set; }
|
||||
public byte A { get; set; }
|
||||
|
||||
public readonly void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public partial struct ColorBGRA32 : IColor<byte>
|
||||
{
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => true;
|
||||
static bool IColor.HasBlueChannel => true;
|
||||
static bool IColor.HasAlphaChannel => true;
|
||||
static bool IColor.ChannelsAreFullyUtilized => true;
|
||||
static Type IColor.ChannelType => typeof(byte);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
/// <typeparam name="TSelf">
|
||||
/// The implementing type.
|
||||
/// </typeparam>
|
||||
/// <typeparam name="TChannel">
|
||||
/// <typeparam name="TChannelValue">
|
||||
/// Supported types are:
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="sbyte"/></item>
|
||||
@@ -32,9 +32,9 @@
|
||||
/// <item><see cref="decimal"/></item>
|
||||
/// </list>
|
||||
/// </typeparam>
|
||||
public interface IColor<TSelf, TChannel> : IColor<TChannel>
|
||||
where TSelf : unmanaged, IColor<TSelf, TChannel>
|
||||
where TChannel : unmanaged
|
||||
public interface IColor<TSelf, TChannelValue> : IColor<TChannelValue>
|
||||
where TSelf : unmanaged, IColor<TSelf, TChannelValue>
|
||||
where TChannelValue : unmanaged
|
||||
{
|
||||
/// <summary>
|
||||
/// A black pixel.
|
||||
|
||||
@@ -42,7 +42,7 @@ public static partial class NumericConversion
|
||||
|
||||
TFrom SignBit = TFrom.One << (Unsafe.SizeOf<TFrom>() * 8 - 1);
|
||||
TFrom converted = (SignBit ^ value);
|
||||
return Unsafe.As<TFrom, TTo>(ref converted);
|
||||
return Unsafe.BitCast<TFrom, TTo>(converted);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
@@ -56,6 +56,6 @@ public static partial class NumericConversion
|
||||
}
|
||||
|
||||
TTo SignBit = TTo.One << (Unsafe.SizeOf<TTo>() * 8 - 1);
|
||||
return SignBit ^ Unsafe.As<TFrom, TTo>(ref value);
|
||||
return SignBit ^ Unsafe.BitCast<TFrom, TTo>(value);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 107 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user